-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #95 from center-for-threat-informed-defense/AF-10_…
…autocomplete AF-10: Autocomplete
- Loading branch information
Showing
56 changed files
with
13,254 additions
and
1,480 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -303,6 +303,32 @@ If this starts up successfully, then you can access the application at | |
http://localhost:8080/. As you edit source code and save, the server will automatically | ||
rebuild the application and you can refresh the browser to run it again. | ||
|
||
Update Intelligence File | ||
~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
The *Intelligence File* (`builder.config.intel.ts`) drives the application's autocomplete features. | ||
This file is generated automatically by a set of scripts which download and organize relevant ATT&CK | ||
information into a format the application can leverage. | ||
|
||
To update the Intelligence File, simply invoke: | ||
|
||
.. code:: shell | ||
$ npm run update-intel | ||
> [email protected] update-intel | ||
> node ./attack/update_attack_intel.js | ||
→ Downloading ATT\&CK Data... | ||
→ .../attack-stix-data/master/enterprise-attack/enterprise-attack-13.0.json | ||
→ ...m/mitre-attack/attack-stix-data/master/ics-attack/ics-attack-13.0.json | ||
→ ...e-attack/attack-stix-data/master/mobile-attack/mobile-attack-13.0.json | ||
→ Generating Application Intel File... | ||
Intelligence updated successfully. | ||
The configured list of sources can be modified at any time from `download_sources.js`. | ||
|
||
Preload a Flow | ||
~~~~~~~~~~~~~~ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
const https = require("https"); | ||
|
||
/** | ||
* @typedef {Object} AttackObject | ||
* An ATT&CK Object. | ||
* @property {string} id | ||
* The object's ATT&CK id. | ||
* @property {string} name | ||
* The object's ATT&CK name. | ||
* @property {string} type | ||
* The object's type. | ||
* @property {string} url | ||
* The object's ATT&CK url. | ||
* @property {string} stixId | ||
* The object's STIX id. | ||
* @property {boolean} deprecated | ||
* True if the ATT&CK object has been deprecated, false otherwise. | ||
* @property {string} matrix | ||
* The ATT&CK object's matrix. | ||
*/ | ||
|
||
/** | ||
* A map that relates STIX types to ATT&CK types. | ||
*/ | ||
const STIX_TO_ATTACK = { | ||
"campaign" : "campaign", | ||
"course-of-action" : "mitigation", | ||
"intrusion-set" : "group", | ||
"malware" : "software", | ||
"tool" : "software", | ||
"x-mitre-data-source" : "data_source", | ||
"x-mitre-tactic" : "tactic", | ||
"attack-pattern" : "technique" | ||
} | ||
|
||
/** | ||
* MITRE's source identifiers. | ||
*/ | ||
const MITRE_SOURCES = new Set([ | ||
"mitre-attack", | ||
"mitre-ics-attack", | ||
"mitre-mobile-attack" | ||
]) | ||
|
||
/** | ||
* Fetches JSON data from a url. | ||
* @param {string} url | ||
* The url. | ||
* @param {Object} options | ||
* The request's options. | ||
* @returns {Promise<Object>} | ||
* A Promise that resolves with the JSON data. | ||
*/ | ||
function fetchJson(url, options = {}) { | ||
return new Promise((resolve, reject) => { | ||
https.get(url, options, res => { | ||
let json = ""; | ||
res.on("data", chunk => { | ||
json += chunk; | ||
}); | ||
res.on("end", () => { | ||
try { | ||
resolve(JSON.parse(json)); | ||
} catch(err) { | ||
reject(err) | ||
} | ||
}) | ||
}).on("error", (err) => { | ||
reject(err); | ||
}); | ||
}) | ||
} | ||
|
||
/** | ||
* Parses an ATT&CK object from a STIX object. | ||
* @param {Object} obj | ||
* The STIX object. | ||
* @param {string} matrix | ||
* The STIX object's matrix. | ||
* @returns {AttackObject} | ||
* The parsed ATT&CK object. | ||
*/ | ||
function parseStixToAttackObject(obj, matrix) { | ||
|
||
// Parse STIX id, name, and type directly | ||
let parse = { | ||
stixId : obj.id, | ||
name : obj.name, | ||
type : STIX_TO_ATTACK[obj.type], | ||
matrix : matrix | ||
} | ||
|
||
// Parse MITRE reference information | ||
let mitreRef = obj.external_references.find( | ||
o => MITRE_SOURCES.has(o.source_name) | ||
); | ||
if(!mitreRef) { | ||
throw new Error("Missing MITRE reference information.") | ||
} | ||
parse.id = mitreRef.external_id; | ||
parse.url = mitreRef.url; | ||
|
||
// Parse deprecation status | ||
parse.deprecated = (obj.x_mitre_deprecated || obj.revoked) ?? false; | ||
|
||
// Return | ||
return parse; | ||
} | ||
|
||
/** | ||
* Parses a set of ATT&CK objects from a STIX manifest. | ||
* @param {Object} data | ||
* The STIX manifest. | ||
* @returns {AttackObject[]} | ||
* The parsed ATT&CK objects. | ||
*/ | ||
function parseAttackObjectsFromManifest(data) { | ||
// Parse matrix | ||
let collection = data.objects.find(o => o.type === "x-mitre-collection"); | ||
if(!collection) { | ||
throw new Error("STIX collection information missing."); | ||
} | ||
// Parse objects | ||
let objs = [] | ||
for(let obj of data.objects) { | ||
if(!(obj.type in STIX_TO_ATTACK)) { | ||
continue; | ||
} | ||
objs.push(parseStixToAttackObject(obj, collection.name)); | ||
} | ||
return objs; | ||
} | ||
|
||
/** | ||
* Fetches ATT&CK data from a set of STIX manifests. | ||
* @param {...string} urls | ||
* A list of STIX manifests specified by url. | ||
* @returns {Promise<Map<string, AttackObject>>} | ||
* A Promise that resolves with the parsed ATT&CK data. | ||
*/ | ||
async function fetchAttackData(...urls) { | ||
console.log("→ Downloading ATT&CK Data..."); | ||
|
||
// Parse objects | ||
let catalog = new Map(); | ||
for(let url of urls) { | ||
console.log(` → ${ url.length > 70 ? '...' : '' }${ url.substr(url.length - 70) }`); | ||
let objs = parseAttackObjectsFromManifest(await fetchJson(url)); | ||
for(let obj of objs) { | ||
catalog.set(obj.stixId, obj); | ||
} | ||
} | ||
|
||
// Categorize catalog | ||
let types = new Map( | ||
Object.values(STIX_TO_ATTACK).map(v => [v, []]) | ||
); | ||
for(let obj of catalog.values()) { | ||
types.get(obj.type).push(obj); | ||
} | ||
|
||
// Return | ||
return types; | ||
|
||
} | ||
|
||
/** | ||
* Define exports. | ||
*/ | ||
module.exports = { fetchAttackData } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* The base URL for the ATT&CK repository. | ||
*/ | ||
const BASE_URL = "https://raw.githubusercontent.com/mitre-attack/attack-stix-data/master"; | ||
|
||
/** | ||
* The STIX sources. | ||
*/ | ||
const STIX_SOURCES = [ | ||
`${BASE_URL}/enterprise-attack/enterprise-attack-13.0.json`, | ||
`${BASE_URL}/ics-attack/ics-attack-13.0.json`, | ||
`${BASE_URL}/mobile-attack/mobile-attack-13.0.json` | ||
] | ||
|
||
/** | ||
* Export | ||
*/ | ||
module.exports = { | ||
STIX_SOURCES | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
const { resolve } = require("path"); | ||
const { writeFileSync } = require("fs"); | ||
const { fetchAttackData } = require("./download_attack"); | ||
const { STIX_SOURCES } = require("./download_sources"); | ||
|
||
/** | ||
* The intel file's export key. | ||
*/ | ||
const EXPORT_KEY = "intel"; | ||
|
||
/** | ||
* The intel file's path. | ||
*/ | ||
const INTEL_FILE_PATH = "../src/assets/builder.config.intel.ts"; | ||
|
||
/** | ||
* JavaScript variable regex. | ||
*/ | ||
const JS_VAR_REGEX = /^[a-z_$][a-z0-9_$]*$/i; | ||
|
||
/** | ||
* Updates the specified intel file. | ||
* @param {string} path | ||
* The intel file's path. | ||
* @param {...string} urls | ||
* A list of STIX manifests specified by url. | ||
*/ | ||
async function updateApplicationAttackIntel(path, ...urls) { | ||
path = resolve(__dirname, path); | ||
|
||
// Validate export key | ||
if(!JS_VAR_REGEX.test(EXPORT_KEY)) { | ||
throw new Error(`Export key '${ EXPORT_KEY }' is not a valid variable name.`); | ||
} | ||
|
||
// Collect intel | ||
let types = await fetchAttackData(...urls); | ||
console.log("→ Generating Application Intel File..."); | ||
let intel = { | ||
tactics : types.get("tactic"), | ||
tactic_recs : types.get("tactic").map(o => `${o.id} (${o.matrix.split(/\s+/)[0]} / ${o.name})`).sort(), | ||
technique : types.get("technique"), | ||
technique_recs : types.get("technique").map(o => `${o.id} (${o.name})`).sort() | ||
}; | ||
|
||
// Generate intel file | ||
let file = ""; | ||
file += `export const ${ EXPORT_KEY } = `; | ||
file += JSON.stringify(intel, null, 4); | ||
file += `;\n\nexport default ${ EXPORT_KEY };\n` | ||
writeFileSync(path, file); | ||
|
||
// Done | ||
console.log("\nIntelligence updated successfully.\n"); | ||
|
||
} | ||
|
||
/** | ||
* Main | ||
*/ | ||
updateApplicationAttackIntel(INTEL_FILE_PATH, ...STIX_SOURCES); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,33 @@ | ||
{ | ||
"name": "attack-flow-builder", | ||
"version": "2.0.1", | ||
"private": true, | ||
"scripts": { | ||
"serve": "vue-cli-service serve", | ||
"build": "vue-cli-service build", | ||
"release": "standard-version" | ||
}, | ||
"author": "mcarenzo", | ||
"dependencies": { | ||
"vue": "^3.0.0", | ||
"vuex": "^4.0.0-0" | ||
}, | ||
"devDependencies": { | ||
"@types/d3": "^7.4.0", | ||
"@types/node": "^20.4.5", | ||
"@types/resize-observer-browser": "^0.1.7", | ||
"@vue/cli-plugin-typescript": "~4.5.15", | ||
"@vue/cli-plugin-vuex": "~4.5.15", | ||
"@vue/cli-service": "~4.5.15", | ||
"@vue/compiler-sfc": "^3.0.0", | ||
"d3": "^7.4.4", | ||
"standard-version": "^9.3.2", | ||
"typescript": "^4.1.5" | ||
}, | ||
"browserslist": [ | ||
"> 1%", | ||
"last 2 versions", | ||
"not dead" | ||
] | ||
} | ||
{ | ||
"name": "attack-flow-builder", | ||
"version": "2.0.1", | ||
"private": true, | ||
"scripts": { | ||
"serve": "vue-cli-service serve", | ||
"build": "vue-cli-service build", | ||
"release": "standard-version", | ||
"update-intel": "node ./attack/update_attack_intel.js" | ||
}, | ||
"author": "mcarenzo", | ||
"dependencies": { | ||
"vue": "^3.0.0", | ||
"vuex": "^4.0.0-0" | ||
}, | ||
"devDependencies": { | ||
"@types/d3": "^7.4.0", | ||
"@types/node": "^20.4.5", | ||
"@types/resize-observer-browser": "^0.1.7", | ||
"@vue/cli-plugin-typescript": "~4.5.15", | ||
"@vue/cli-plugin-vuex": "~4.5.15", | ||
"@vue/cli-service": "~4.5.15", | ||
"@vue/compiler-sfc": "^3.0.0", | ||
"d3": "^7.4.4", | ||
"standard-version": "^9.3.2", | ||
"typescript": "^4.1.5" | ||
}, | ||
"browserslist": [ | ||
"> 1%", | ||
"last 2 versions", | ||
"not dead" | ||
] | ||
} |
Oops, something went wrong.