Skip to content

Commit

Permalink
Merge branch 'main' into AF-10_autocomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
mikecarenzo committed Aug 24, 2023
2 parents be0afd8 + ecb1175 commit 56c5587
Show file tree
Hide file tree
Showing 19 changed files with 545 additions and 25 deletions.
16 changes: 11 additions & 5 deletions src/attack_flow_builder/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<div id="app-body" ref="body" :style="gridLayout">
<div class="frame center">
<BlockDiagram id="block-diagram"/>
<SplashMenu id="splash-menu" />
</div>
<div class="frame right">
<div class="resize-handle" @pointerdown="startResize($event, Handle.Right)"></div>
Expand All @@ -23,14 +24,16 @@ import Configuration from "@/assets/builder.config"
import { clamp } from "./assets/scripts/BlockDiagram";
import { PointerTracker } from "./assets/scripts/PointerTracker";
import { mapMutations, mapState } from 'vuex';
import { LoadFile, LoadSettings } from './store/Commands/AppCommands';
import * as App from './store/Commands/AppCommands';
import { defineComponent, markRaw, ref } from 'vue';
// Components
import SplashMenu from "@/components/Controls/SplashMenu.vue";
import AppTitleBar from "@/components/Elements/AppTitleBar.vue";
import AppHotkeyBox from "@/components/Elements/AppHotkeyBox.vue";
import BlockDiagram from "@/components/Elements/BlockDiagram.vue";
import AppFooterBar from "@/components/Elements/AppFooterBar.vue";
import EditorSidebar from "@/components/Elements/EditorSidebar.vue";
import { ShowSplashMenu } from "./store/Commands/AppCommands/ShowSplashMenu";
const Handle = {
None : 0,
Expand Down Expand Up @@ -156,20 +159,22 @@ export default defineComponent({
settings = require("../public/settings.json");
}
// Load settings
this.execute(new LoadSettings(this.context, settings));
this.execute(new App.LoadSettings(this.context, settings));
// Load empty file
this.execute(await LoadFile.fromNew(this.context));
this.execute(await App.LoadFile.fromNew(this.context));
// Load file from query parameters, if possible
let params = new URLSearchParams(window.location.search);
let src = params.get("src");
if(src) {
try {
// TODO: Incorporate loading dialog
this.execute(await LoadFile.fromUrl(this.context, src));
this.execute(await App.LoadFile.fromUrl(this.context, src));
} catch(ex) {
console.error(`Failed to load file from url: '${ src }'`);
console.error(ex);
}
} else {
this.execute(new ShowSplashMenu(this.context));
}
},
mounted() {
Expand All @@ -193,7 +198,8 @@ export default defineComponent({
AppTitleBar,
BlockDiagram,
AppFooterBar,
EditorSidebar
EditorSidebar,
SplashMenu,
},
});
</script>
Expand Down
Binary file added src/attack_flow_builder/src/assets/afb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions src/attack_flow_builder/src/assets/builder.config.publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ class AttackFlowPublisher extends DiagramPublisher {
break;
case PropertyType.List:
if (prop.isDefined()) {
if(key === "hashes") {
this.mergeComplexListProperty(node, key, prop as ListProperty);
break;
}
this.mergeBasicListProperty(node, key, prop as ListProperty);
}
break;
Expand Down Expand Up @@ -296,6 +300,35 @@ class AttackFlowPublisher extends DiagramPublisher {
}
}

/**
* Merges a more complicated list into a STIX node.
* @param node
* The STIX node.
* @param key
* The list property's key.
* @param property
* The list property.
*/
private mergeComplexListProperty(node: Sdo, key: string, property: ListProperty) {
switch(key) {
case "hashes":
let hashList = [];
for(let prop of property.value.values()) {
switch(prop.type) {
case PropertyType.Dictionary:
if(prop.isDefined()) {
let entries = prop.toRawValue() as RawEntries;
hashList.push(Object.fromEntries(entries));
}
break;
}
}
if (hashList.length > 0) {
let hashes = hashList.map(hash => [hash.hash_type, hash.hash_value]); // Drop the property labels
node[key] = Object.fromEntries(hashes);
}
}
}

///////////////////////////////////////////////////////////////////////////
// 2. Relationships Embeddings //////////////////////////////////////////
Expand Down
110 changes: 107 additions & 3 deletions src/attack_flow_builder/src/assets/builder.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,35 @@ const config: AppConfiguration = {
application_name: "Attack Flow Builder",
file_type_name: "Attack Flow",
file_type_extension: "afb",
menu_icon: "./ctid-small.png",
splash: {
product: "./afb.png",
organization: "./ctid.png",
buttons: [
{
action: "new",
name: "New Flow",
description: "Create a new, blank flow",
},
{
action: "open",
name: "Open Flow",
description: "Open an existing flow from your computer"
},
{
action: "link",
name: "Example Flows",
description: "View a list of example flows",
url: "https://center-for-threat-informed-defense.github.io/attack-flow/example_flows/"
},
{
action: "link",
name: "Builder Help",
description: "View help for Attack Flow Builder",
url: "https://center-for-threat-informed-defense.github.io/attack-flow/builder/"
},
],
},
schema: {
page_template: "flow",
templates: [
Expand Down Expand Up @@ -557,7 +586,32 @@ const config: AppConfiguration = {
mime_type : { type: PropertyType.String },
payload_bin : { type: PropertyType.String },
url : { type: PropertyType.String },
hashes : { type: PropertyType.String },
hashes : {
type: PropertyType.List,
form: {
type: PropertyType.Dictionary,
form: {
hash_type: {
type: PropertyType.Enum,
options: {
type: PropertyType.List,
form: { type: PropertyType.String },
value: [
["custom", "Custom Hash Key"],
["md5", "MD5"],
["sha-1", "SHA-1"],
["sha-256", "SHA-256"],
["sha-512", "SHA-512"],
["sha3-256", "SHA3-256"],
["ssdeep", "SSDEEP"],
["tlsh", "TLSH"]
]
}
},
hash_value: { type: PropertyType.String, is_primary: true }
}
}
},
encryption_algorithm : { type: PropertyType.String },
decryption_key : { type: PropertyType.String },
},
Expand Down Expand Up @@ -640,7 +694,32 @@ const config: AppConfiguration = {
type: TemplateType.DictionaryBlock,
role: SemanticRole.Node,
properties: {
hashes : { type: PropertyType.String },
hashes : {
type: PropertyType.List,
form: {
type: PropertyType.Dictionary,
form: {
hash_type: {
type: PropertyType.Enum,
options: {
type: PropertyType.List,
form: { type: PropertyType.String },
value: [
["custom", "Custom Hash Key"],
["md5", "MD5"],
["sha-1", "SHA-1"],
["sha-256", "SHA-256"],
["sha-512", "SHA-512"],
["sha3-256", "SHA3-256"],
["ssdeep", "SSDEEP"],
["tlsh", "TLSH"]
]
}
},
hash_value: { type: PropertyType.String, is_primary: true }
}
}
},
size : { type: PropertyType.String },
name : { type: PropertyType.String, is_primary: true },
name_enc : { type: PropertyType.String },
Expand Down Expand Up @@ -806,7 +885,32 @@ const config: AppConfiguration = {
properties: {
subject : { type: PropertyType.String, is_primary: true, is_required: true },
is_self_signed : BoolEnum,
hashes : { type: PropertyType.String },
hashes : {
type: PropertyType.List,
form: {
type: PropertyType.Dictionary,
form: {
hash_type: {
type: PropertyType.Enum,
options: {
type: PropertyType.List,
form: { type: PropertyType.String },
value: [
["custom", "Custom Hash Key"],
["md5", "MD5"],
["sha-1", "SHA-1"],
["sha-256", "SHA-256"],
["sha-512", "SHA-512"],
["sha3-256", "SHA3-256"],
["ssdeep", "SSDEEP"],
["tlsh", "TLSH"]
]
}
},
hash_value: { type: PropertyType.String, is_primary: true }
}
}
},
version : { type: PropertyType.String },
serial_number : { type: PropertyType.String },
signature_algorithm : { type: PropertyType.String },
Expand Down
62 changes: 62 additions & 0 deletions src/attack_flow_builder/src/assets/builder.config.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ListProperty,
Property,
PropertyType,
RawEntries,
SemanticAnalyzer
} from "./scripts/BlockDiagram";

Expand Down Expand Up @@ -153,11 +154,28 @@ class AttackFlowValidator extends DiagramValidator {
}
// Validate links
switch(node.template.id) {
case "artifact": {
const hashes = node.props.value.get("hashes");
if(hashes?.isDefined()) {
this.validateHash(id, hashes as ListProperty);
}
break;
}
case "email_address": // Additional validation for email addresses
if (!AttackFlowValidator.Emailregex.test(String(node.props.value.get("value")))) {
this.addError(id, "Invalid email address.")
}
break;
case "file":
const hash = node.props.value.get("hashes");
const name = node.props.value.get("name");
if(!hash?.isDefined() && !name?.isDefined()) {
this.addError(id, "File requires one of the following properties: Hashes, Name");
}
if(hashes?.isDefined()) {
this.validateHash(id, hashes as ListProperty);
}
break;
case "grouping":
if(node.next.length === 0) {
this.addError(id, "A Grouping must point to at least one object.");
Expand Down Expand Up @@ -222,6 +240,13 @@ class AttackFlowValidator extends DiagramValidator {
this.addError(id, "Invalid Windows registry key.");
}
break;
case "x509_certificate": {
const hashes = node.props.value.get("hashes");
if(hashes?.isDefined()) {
this.validateHash(id, hashes as ListProperty);
}
break;
}
}
}

Expand Down Expand Up @@ -297,6 +322,43 @@ class AttackFlowValidator extends DiagramValidator {
}
}

/**
* Validate a hash from any hash-containing node.
*
* Reference: https://docs.oasis-open.org/cti/stix/v2.1/os/stix-v2.1-os.html#_odoabbtwuxyd
*
* @param id
* The node's id.
* @param hashes
* The list of hashes to validate.
*/
protected validateHash(id: string, hashes: ListProperty) {
const validKey = /^[a-zA-Z0-9_-]{3,250}$/;
const hashDictionaryProps = hashes.value;

if(!hashDictionaryProps) {
// This is an older AFB file that does not have the Hash property updated to current version.
this.addWarning(id, "This AFB is outdated, please remake it in a new file.");
return;
}

for(let hashDictionary of hashDictionaryProps.values()) {
if(!hashDictionary.isDefined()) {
this.addError(id, "Hash Value cannot be empty.");
}
// Make sure hash_type is not empty.
let entries = hashDictionary.toRawValue()! as RawEntries;
if(!Object.fromEntries(entries).hash_type) {
this.addError(id, "Hash Type cannot be left empty.");
}

let key = hashDictionary.toString();
if (!validKey.test(key)) {
this.addError(id, "Invalid hash key.");
}
}
}

/**
* Validates the links to/from a network-traffic node.
*
Expand Down
Binary file added src/attack_flow_builder/src/assets/ctid-small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/attack_flow_builder/src/assets/ctid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 56c5587

Please sign in to comment.