Skip to content

Commit

Permalink
Merge pull request #353 from center-for-threat-informed-defense/develop
Browse files Browse the repository at this point in the history
Release Workbench 2.2.0
  • Loading branch information
jondricek authored Oct 31, 2024
2 parents b6f87b2 + 703750d commit 18f58fb
Show file tree
Hide file tree
Showing 20 changed files with 185 additions and 110 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ jobs:
name: static-checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18.x'
- run: npm install
Expand Down
2 changes: 1 addition & 1 deletion NOTICE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright 2020-2023 MITRE Engenuity. Approved for public release. Document number CT0020 and public release case number 22-3206.
Copyright 2020-2024 MITRE Engenuity. Approved for public release. Document number CT0020 and public release case number 22-3206.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ The workflow is defined in `.github/workflows/ci-workflow.yml`

## Notice

Copyright 2020-2023 MITRE Engenuity. Approved for public release. Document number CT0020
Copyright 2020-2024 MITRE Engenuity. Approved for public release. Document number CT0020

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/assets-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ paths:
- name: search
in: query
description: |
Only return objects where the provided search text occurs in the `name` or `description`.
Only return objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/attack-objects-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ paths:
- name: search
in: query
description: |
Only return ATT&CK objects where the provided search text occurs in the `name` or `description`.
Only return ATT&CK objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/campaigns-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ paths:
- name: search
in: query
description: |
Only return objects where the provided search text occurs in the `name` or `description`.
Only return objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/data-sources-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ paths:
- name: search
in: query
description: |
Only return objects where the provided search text occurs in the `name` or `description`.
Only return objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/groups-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ paths:
- name: search
in: query
description: |
Only return objects where the provided search text occurs in the `name` or `description`.
Only return objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/mitigations-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ paths:
- name: search
in: query
description: |
Only return objects where the provided search text occurs in the `name` or `description`.
Only return objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/software-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ paths:
- name: search
in: query
description: |
Only return objects where the provided search text occurs in the `name` or `description`.
Only return objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/tactics-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ paths:
- name: search
in: query
description: |
Only return objects where the provided search text occurs in the `name` or `description`.
Only return objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
2 changes: 1 addition & 1 deletion app/api/definitions/paths/techniques-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ paths:
- name: search
in: query
description: |
Only return objects where the provided search text occurs in the `name` or `description`.
Only return objects where the provided search text occurs in the `attack_id`, `name`, or `description`.
The search is case-insensitive.
schema:
type: string
Expand Down
20 changes: 9 additions & 11 deletions app/config/allowed-values.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@
"macOS",
"Windows",
"Network",
"Office 365",
"Azure AD",
"SaaS",
"IaaS",
"Google Workspace",
"Containers"
"Containers",
"Identity Provider",
"Office Suite"
]
},
{
Expand Down Expand Up @@ -159,11 +158,11 @@
"macOS",
"Windows",
"Network",
"Office 365",
"Azure AD",
"IaaS",
"SaaS",
"Containers"
"Containers",
"Identity Provider",
"Office Suite"
]
},
{
Expand Down Expand Up @@ -231,14 +230,13 @@
"allowedValues": [
"Containers",
"Windows",
"Azure AD",
"Linux",
"macOS",
"IaaS",
"SaaS",
"Office 365",
"Google Workspace",
"Network"
"Network",
"Identity Provider",
"Office Suite"
]
},
{
Expand Down
1 change: 1 addition & 0 deletions app/services/attack-objects-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ exports.retrieveAll = async function(options) {
const match = {
$match: {
$or: [
{ 'workspace.attack_id': { '$regex': options.search, '$options': 'i' }},
{ 'stix.name': { '$regex': options.search, '$options': 'i' } },
{ 'stix.description': { '$regex': options.search, '$options': 'i' } }
]
Expand Down
4 changes: 2 additions & 2 deletions app/services/matrices-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,14 +274,14 @@ exports.retrieveTechniquesForMatrix = function(stixId, modified, callback) {
for (const parentTechnique of parentTechniques) {
parentTechnique.subtechniques = [];
for (const subtechnique of subtechniques) {
if (subtechnique.workspace.attack_id.split(".")[0] === parentTechnique.workspace.attack_id) {
if (subtechnique.workspace.attack_id && subtechnique.workspace.attack_id.split(".")[0] === parentTechnique.workspace.attack_id) {
parentTechnique.subtechniques.push(subtechnique);
}
}
}
// Add techniques to tactic & store tactic
tactic.techniques = parentTechniques;
tacticsTechniques[tactic.stix.name] = tactic;
tacticsTechniques[tactic.stix.id] = tactic;
}
}
return callback(null, tacticsTechniques);
Expand Down
18 changes: 17 additions & 1 deletion app/services/stix-bundles-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ exports.exportBundle = async function(options) {
}

// Put the primary objects in the bundle
// Also create a map of the objects added to the bundle (only use the id, since relationships only reference the id)
// Also create a map of the objects added to the bundle (use the id as the key, since relationships only reference the id)
const objectsMap = new Map();
for (const primaryObject of primaryObjects) {
bundle.objects.push(primaryObject.stix);
Expand Down Expand Up @@ -342,6 +342,22 @@ exports.exportBundle = async function(options) {
addAttackObjectToMap(secondaryObject);
}

// Add groups to the bundle that are referenced by a campaign but are not referenced by a primary object
for (const relationship of allRelationships) {
if (relationship.stix.relationship_type === 'attributed-to') {
if (objectsMap.has(relationship.stix.source_ref) && !objectsMap.has(relationship.stix.target_ref)) {
// Add the group to the bundle
const groupObject = await getAttackObject(relationship.stix.target_ref);
if (groupObject.stix.type === 'intrusion-set' && secondaryObjectIsValid(groupObject)) {
groupObject.stix.x_mitre_domains = [ options.domain ];
bundle.objects.push(groupObject.stix);
objectsMap.set(groupObject.stix.id, true);
// relationships will be added to the bundle later
}
}
}
}

// Data components have already been added to the bundle because they're referenced in a relationship
// Get the data sources referenced by data components, using a map to eliminate duplicates
const dataSourceIds = new Map();
Expand Down
110 changes: 106 additions & 4 deletions app/tests/api/stix-bundles/stix-bundles.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ const initialObjectData = {
"object_ref": "intrusion-set--8a831aaa-f3e0-47a3-bed8-a9ced744dd12",
"object_modified": "2020-06-03T20:22:40.401Z"
},
{
"object_ref": "intrusion-set--ed0222fb-b970-4337-b9a2-62aeb02860e5",
"object_modified": "2023-05-02T20:19:40.401Z"
},
{
"object_ref": "relationship--12098dee-27b3-4d0b-a15a-6b5955ba8879",
"object_modified": "2019-09-04T14:32:13.000Z"
Expand All @@ -76,6 +80,10 @@ const initialObjectData = {
"object_ref": "campaign--a3038910-f8ca-4ba8-b116-21d0f333f231",
"object_modified": "2020-07-03T20:22:40.401Z"
},
{
"object_ref": "campaign--649b389e-1f7a-4696-8a95-04d0851bd551",
"object_modified": "2020-07-03T20:22:40.401Z"
},
{
"object_ref": "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
"object_modified": "2017-06-01T00:00:00.000Z"
Expand Down Expand Up @@ -127,7 +135,15 @@ const initialObjectData = {
{
"object_ref": "relationship--a5a80c31-0dde-4fd7-a520-a7593d21c954",
"object_modified": "2021-06-08T14:00:00.000Z"
}
},
{
"object_ref": "relationship--1e1c5e5a-2a3e-423f-b1d0-67b7dc5b90cc",
"object_modified": "2023-06-08T14:00:00.000Z"
},
{
"object_ref": "relationship--d5426745-9530-485e-a757-d8c540f600f8",
"object_modified": "2021-06-06T14:00:00.000Z"
},
]
},
{
Expand Down Expand Up @@ -343,6 +359,30 @@ const initialObjectData = {
spec_version: "2.1",
x_mitre_version: "1.2"
},
{
type: "intrusion-set",
id: "intrusion-set--ed0222fb-b970-4337-b9a2-62aeb02860e5",
created_by_ref: "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
name: "Another group",
description: "This is another group. It isn't referenced by a technique, but is associated with a campaign",
object_marking_refs: [
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
],
external_references: [
{
source_name: "mitre-attack",
url: "https://attack.mitre.org/groups/G0999",
external_id: "G0999"
}
],
aliases: [
"Some group alias"
],
modified: "2023-05-02T20:19:40.401Z",
created: "2018-10-17T00:19:20.652Z",
spec_version: "2.1",
x_mitre_version: "1.2"
},
{
type: "campaign",
id: "campaign--a3038910-f8ca-4ba8-b116-21d0f333f231",
Expand Down Expand Up @@ -371,6 +411,34 @@ const initialObjectData = {
spec_version: "2.1",
x_mitre_version: "1.2"
},
{
type: "campaign",
id: "campaign--649b389e-1f7a-4696-8a95-04d0851bd551",
created_by_ref: "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
name: "campaign-2",
description: "This is another campaign",
first_seen: "2016-04-06T00:00:00.000Z",
last_seen: "2016-07-12T00:00:00.000Z",
x_mitre_first_seen_citation: "(Citation: Article 1)",
x_mitre_last_seen_citation: "(Citation: Article 2)",
object_marking_refs: [
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
],
external_references: [
{
source_name: "mitre-attack",
url: "https://attack.mitre.org/campaigns/C0002",
external_id: "C0002"
}
],
aliases: [
"Another campaign name"
],
modified: "2020-07-03T20:22:40.401Z",
created: "2018-11-17T00:14:20.652Z",
spec_version: "2.1",
x_mitre_version: "1.2"
},
{
created_by_ref: "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
object_marking_refs: [
Expand Down Expand Up @@ -534,6 +602,21 @@ const initialObjectData = {
spec_version: "2.1"
},
{
created_by_ref: "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
object_marking_refs: [
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
],
source_ref: "campaign--649b389e-1f7a-4696-8a95-04d0851bd551",
target_ref: "attack-pattern--82f04b1e-5371-4a6f-be06-411f0f43b483",
external_references: [],
description: "Campaign uses technique",
relationship_type: "uses",
id: "relationship--d5426745-9530-485e-a757-d8c540f600f8",
type: "relationship",
modified: "2021-06-06T14:00:00.000Z",
created: "2021-06-06T14:00:00.000Z",
spec_version: "2.1"
}, {
created_by_ref: "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
object_marking_refs: [
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
Expand Down Expand Up @@ -564,6 +647,22 @@ const initialObjectData = {
modified: "2021-06-08T14:00:00.000Z",
created: "2021-06-08T14:00:00.000Z",
spec_version: "2.1"
},
{
created_by_ref: "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5",
object_marking_refs: [
"marking-definition--fa42a846-8d90-4e51-bc29-71d5b4802168"
],
source_ref: "campaign--649b389e-1f7a-4696-8a95-04d0851bd551",
target_ref: "intrusion-set--ed0222fb-b970-4337-b9a2-62aeb02860e5",
external_references: [],
description: "Campaign attributed to group and is the only reference to the group",
relationship_type: "attributed-to",
id: "relationship--1e1c5e5a-2a3e-423f-b1d0-67b7dc5b90cc",
type: "relationship",
modified: "2023-06-08T14:00:00.000Z",
created: "2023-06-08T14:00:00.000Z",
spec_version: "2.1"
}
]
};
Expand Down Expand Up @@ -632,7 +731,7 @@ describe('STIX Bundles Basic API', function () {
// We expect to get the created collection object
const collection = res.body;
expect(collection).toBeDefined();
expect(collection.workspace.import_categories.additions.length).toBe(24);
expect(collection.workspace.import_categories.additions.length).toBe(initialObjectData.objects[0].x_mitre_contents.length);
expect(collection.workspace.import_categories.errors.length).toBe(0);
done();
}
Expand Down Expand Up @@ -751,9 +850,12 @@ describe('STIX Bundles Basic API', function () {
const stixBundle = res.body;
expect(stixBundle).toBeDefined();
expect(Array.isArray(stixBundle.objects)).toBe(true);
// 2 primary objects, 2 relationship objects, 3 secondary object,
// 3 primary objects, 5 relationship objects, 5 secondary objects,
// 1 identity, 1 marking definition
expect(stixBundle.objects.length).toBe(9);
expect(stixBundle.objects.length).toBe(15);

const groupObjects = stixBundle.objects.filter(o => o.type === 'intrusion-set');
expect(groupObjects.length).toBe(2);

done();
}
Expand Down
Loading

0 comments on commit 18f58fb

Please sign in to comment.