Skip to content

Commit

Permalink
Merge pull request #42 from dod-cyber-crime-center/incident_core
Browse files Browse the repository at this point in the history
STIX Incident - Editorial Fixes
  • Loading branch information
ejratl authored Oct 10, 2023
2 parents 40bfcde + 8220134 commit 507df84
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

[.stix-doc-information-heading]#Draft#

[.stix-doc-information-heading]#10 July 2023#
[.stix-doc-information-heading]#10 October 2023#

[.stix-doc-information-heading]
Editors:
Expand Down Expand Up @@ -108,7 +108,7 @@ The properties and additional types within the Incident Core Extension are defin
|*determination* (required)
|[stixtype]#<<incident-determination-enum,incident-determination-enum>>#
|A high level determination on the outcome of this incident.
This *SHOULD* be suspected until enough information is available to provide a well researched result.
This *SHOULD* be [stixliteral]#suspected# until enough information is available to provide a well researched result.

Some automated tools may flag results as blocked or low-value automatically depending on the tool type or activity.
A tool that blocks a series of phishing emails may create an incident with a blocked determination automatically.
Expand Down Expand Up @@ -143,16 +143,15 @@ This should draw from the [stixtype]#<<detection-methods-ov,detection-methods-ov
|*impact_refs* (optional)
|[stixtype]#{list_url}[list]# of type [stixtype]#{identifier_url}[identifier]#
|A list of impacts of this incident.
It *MUST* contain references to one or more Impact objects.

The objects referenced in this list *MUST* be of type [stixtype]#<<impact,impact>>#
All objects referenced in this list *MUST* be of type [stixtype]#<<impact,impact>>#

|*impacted_entity_counts* (optional)
|[stixtype]#<<entity-count,entity-count>>#
|An optional listing of the entity types that were impacted by the incident, and how many of each type were affected.
Individual impacts may also record more detailed counts as appropriate.

If this field is not present it should be assumed that this information is not being shared, not that there were no impacted entities.
If this property is not present it should be assumed that this information is not being shared, not that there were no impacted entities.
To affirmatively state no entities of a given class were impacted they should be included with the number of entities affected by it set to [stixliteral]#0#.

|*incident_types* (optional)
|[stixtype]#{list_url}[list]# of type [stixtype]#{open_vocab_url}[open-vocab]#
Expand Down Expand Up @@ -379,7 +378,8 @@ accurate up to the number of decimals it includes.
|[stixrelationship]#led-to#
|[stixtype]#<<event,event>>#
|One event led to another.
For example a dropper running allowed a ransomware tool to be downloaded and run.

For example, a dropper running allowed a ransomware tool to be downloaded and run.

|[stixtype]#<<event,event>>#
|[stixrelationship]#impacts#
Expand All @@ -392,12 +392,6 @@ For example a dropper running allowed a ransomware tool to be downloaded and run
|[stixtype]#{location_url}[location]#
|The event occurred at a specific location or locations.

|[stixtype]#<<event,event>>#
|[stixrelationship]#observed#
|<All STIX Cyber-observable Objects>
|STIX cyber-observables were observed as part of this event, but no information on when they are observed is being shared.

If this can be shared a Sighting it should be instead of using this method.
// relationships:end
|===

Expand Down Expand Up @@ -496,15 +490,15 @@ This can be translated into qualitative values as described in <<appendix-b,Appe
|[stixtype]#{timestamp_url}[timestamp]#
|The date and time the impact was last recorded.

This property *SHOULD* be populated.
This property *SHOULD* be populated if the impact is resolved or mitigated.

If the *superseded_by_ref* property is included this *MUST* be included.

|*end_time_fidelity* (optional)
|[stixtype]#<<timestamp-fidelity-enum,timestamp-fidelity-enum>>#
|The level of fidelity that the end_time is recorded in.
This value *MUST* come from [stixtype]#<<timestamp-fidelity-enum,timestamp-fidelity-enum>>#.

If the *superseded_by_ref* property is included this *MUST* be included.

|*impacted_entity_counts* (optional)
|[stixtype]#<<entity-count,entity-count>>#
|An optional listing of the entity types that were impacted and how many of each were affected.
Expand Down Expand Up @@ -944,15 +938,6 @@ Using these embedded relationships ensure that an incomplete sequence cannot be
|[stixtype]#{location_url}[location]#
|The task occurred at a specific location or locations.

|[stixtype]#<<task,task>>#
|[stixrelationship]#errored-to#
|[stixtype]#<<task,task>>#
|A task follows this one because of an error / if statement in a playbook.

|[stixtype]#<<task,task>>#
|[stixrelationship]#followed-by#
|[stixtype]#<<task,task>>#
|A task follows this one in the normal chain of execution.
// relationships:end
|===

Expand Down Expand Up @@ -1890,53 +1875,8 @@ The destruction or encryption of this data can cause availability impacts.

<<<

=== 4.9. State Change Object Type

*Type Name:* [stixtype]#state-change#

The *initial_ref* or *result_ref* *MUST* be populated.

[width="100%",cols="37%,23%,40%",options="header",]
|===
^|[stixtr]*Property Name*
^|[stixtr]*Type*
^|[stixtr]*Description*

|*state_change_type* (required)
|[stixtype]#{open_vocab_url}[open-vocabulary]#
|How this activity influenced the change in state between the *initial_ref* and *result_ref*.

This *SHOULD* be drawn from [stixtype]#<<state-change-type-ov,state-change-type-ov>>#.

|*initial_ref* (optional)
|[stixtype]#{identifier_url}[identifier]#
|The initial object state that this event affected.

If the *result_ref* is not populated then this *MUST* be populated.
If there is no result state this typically means that this event removed or resolved the initial object.
For example, an event or task resolved a network outage.

If both are present this indicates a transition between these states.
For example, a confidentiality impact was made worse as the information was shared further.

If the *result_ref* is populated this *MUST* reference the same type of SDO.

|*result_ref* (optional)
|[stixtype]#{identifier_url}[identifier]#
|The final state that this event influenced.

If the *initial_ref* is not populated then this *MUST* be populated.
If there is no initial state it typically means that this event caused or created the result.
For example, an event causing a network outage.

If the *initial_ref* is populated this *MUST* reference the same type of SDO.

|===

<<<

[[state-change-type-ov]]
=== 4.10. State Change Type Vocabulary
=== 4.9. State Change Type Vocabulary

*Type Name:* [stixtype]#state-change-type-ov#

Expand Down Expand Up @@ -1971,7 +1911,7 @@ If this is selected *result_ref* MUST be populated.
<<<

[[task-type-ov]]
=== 4.11. Task Type Vocabulary
=== 4.10. Task Type Vocabulary

*Type Name:* [stixtype]#task-type-ov#

Expand Down Expand Up @@ -2717,4 +2657,9 @@ Added [stixliteral]#ransom-demand# and [stixliteral]#ransom-payment# to [stixtyp
|Incident Mini Group
|Version 2.0

|05
|2023-10-10
|Richard Piazza and Jeffrey Mates
|Multiple editorial fixes, removing copy paste errors and obsolete relationships.

|===
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,6 @@
|[stixrelationship]#detects#
|[stixtype]#<<event,event>>#

|[stixtype]#<<task,task>>#
|[stixrelationship]#errored-to#
|[stixtype]#<<task,task>>#

|[stixtype]#<<task,task>>#
|[stixrelationship]#followed-by#
|[stixtype]#<<task,task>>#

|[stixtype]#<<task,task>>#
|[stixrelationship]#impacts#
|[stixtype]#{infrastructure_url}[infrastructure]#
Expand Down
136 changes: 136 additions & 0 deletions scripts/validate_adoc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import argparse
import pathlib
import re
import sys

class Validator():
def __init__(self, file_path):
self._errors = []

with open(file_path, "rt") as input_file:
self._content = input_file.read()

def run_jobs(self) -> list:
self.consistent_type_declaration(self._content)
self.type_declarations_references(self._content)
self.external_reference_variables(self._content)
self.section_ids(self._content)

return self._errors

# Verify that all internal references can be resolved and that all anchors for references are used
def consistent_type_declaration(self, content) -> None:
find_references = re.compile("<<([a-zA-Z\-_]+),[a-zA-Z\-_]+")
find_declarations = re.compile("\[\[([a-zA-Z\-_]+)\]\]")
references = set(find_references.findall(content))
declarations = set(find_declarations.findall(content))

for item in references.difference(declarations):
self._errors.append(f"{item} was referenced but not declared")

for item in declarations.difference(references):
self._errors.append(f"{item} was declared but not referenced")

# Verify all types are references
def type_declarations_references(self, content) -> None:
find_invalid_type = re.compile("\[stixtype\]#([^\{<][^#]+)")
find_appendix = re.compile("== Appendix [A-Z]\. Revision History")

line_number = 0
for line in content.split("\n"):
line_number += 1

# Type definitions don't need to link to themselves and once we start the revision history we're done
# since it can reference types that no longer exist meaning we can't have links
if line.startswith("*Type Name:* "):
continue
elif find_appendix.match(line.strip()):
break

results = find_invalid_type.findall(line)

for item in results:
self._errors.append(f"Line {line_number}: {item} has no reference")

# Verify all external type definitions use valid variables
def external_reference_variables(self, content) -> None:
variable_declaration = re.compile(":([a-z_]+): https://")
variable_use = re.compile("\].?\{([^\}]+)") # using a generic character after the type in case * is used instead of #

variables = set(variable_declaration.findall(content))

line_number = 0
for line in content.split("\n"):
line_number += 1

results = variable_use.findall(line)

for item in results:
if item not in variables:
self._errors.append(f"Line {line_number}: {item} is an external reference that is not declared as a variable")

# Verify section IDs increase and none are skipped
def section_ids(self, content) -> None:
last_id = None
last_parts = []
find_formatted_id = re.compile("^(=+ (?:\d+\.?)+)")


line_number = 0
for line in content.split("\n"):
line_number += 1
results = find_formatted_id.findall(line)


for item in results:
id = item.split(" ")[-1][:-1]
parts = id.split(".")

if (item.count("=") - 1) != item.count("."):
self._errors.append(f"Line {line_number}: {item} has the incorrect list level given its numbering")

if last_id is None:
last_id = id
last_parts = parts
if id != "1":
self._errors.append(f"Line {line_number}: {item} should be 1 as the first item")
continue

if len(parts) == len(last_parts):
if int(last_parts[-1]) != int(parts[-1]) - 1:
self._errors.append(f"Line {line_number}: {item} should be {'.'.join(last_parts[:-1])}.{int(last_parts[-1]) + 1}")
elif len(parts) > len(last_parts):
if not id.startswith(last_id):
self._errors.append(f"Line {line_number}: {item} should be numbered {last_id}.0 or {last_id}.1")
elif parts[-1] != "1":
self._errors.append(f"Line {line_number}: {item} should be numbered {last_id}.0 or {last_id}.1")
else:
last_parts = last_parts[:len(parts)]
expected = f"{'.'.join(last_parts[:-1])}.{int(last_parts[-1]) + 1}".strip(".")
if id != expected:
self._errors.append(f"Line {line_number}: {item} should be numbered {expected}")

last_id = id
last_parts = parts



if __name__ == "__main__":
arguments = argparse.ArgumentParser(
prog="Validate STIX ASCII Doc",
description="Validates an ASCII Doc input file meets STIX 2.1 format standards"
)

arguments.add_argument("-i", "--input", dest="input_path", action="store", required=True, help="The input file")
args = arguments.parse_args()

validator = Validator(args.input_path)
errors = validator.run_jobs()

if len(errors) > 0:
print("\n".join(errors), file=sys.stderr)
exit(1)
else:
print("No errors found")

exit(0)

0 comments on commit 507df84

Please sign in to comment.