From 904ab390f9dc3e01ec787e52dbeb21daf2ab0dc0 Mon Sep 17 00:00:00 2001 From: Charissa Miller <48832936+clemiller@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:30:22 -0400 Subject: [PATCH] patch linkbyid logic --- app/src/app/classes/stix/stix-object.ts | 4 +- .../save-dialog/save-dialog.component.ts | 1 + .../descriptive-view.component.ts | 49 ++++++++++++------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/app/src/app/classes/stix/stix-object.ts b/app/src/app/classes/stix/stix-object.ts index cc360b04..38fec183 100644 --- a/app/src/app/classes/stix/stix-object.ts +++ b/app/src/app/classes/stix/stix-object.ts @@ -520,7 +520,7 @@ export abstract class StixObject extends Serializable { let ids = []; for (let link of links) { let id = link.split("(LinkById: ")[1].slice(0, -1); - if(!ids.includes(id)) ids.push(id); + if(!ids.includes(id) && id != '') ids.push(id); } return restAPIService.getAllObjects({attackIDs: ids, revoked: true, deprecated: true, deserialize: true}).pipe( @@ -528,7 +528,7 @@ export abstract class StixObject extends Serializable { // objects must be validated in cases where more than one object is // returned by the given ATT&CK ID, this occurs due to older versions // of ATT&CK in which techniques shared their IDs with mitigations - let validObjects = (results.data as StixObject[]).filter(obj => obj.isValidAttackId()); + let validObjects = (results.data as StixObject[]).filter(obj => obj.supportsAttackID && obj.isValidAttackId()); let retrieved_ids = validObjects.map(obj => obj.attackID); for (let id of ids) { if (!retrieved_ids.includes(id)) result.missingLinks.add(id); diff --git a/app/src/app/components/save-dialog/save-dialog.component.ts b/app/src/app/components/save-dialog/save-dialog.component.ts index b455c0b2..256d9ad5 100644 --- a/app/src/app/components/save-dialog/save-dialog.component.ts +++ b/app/src/app/components/save-dialog/save-dialog.component.ts @@ -66,6 +66,7 @@ export class SaveDialogComponent implements OnInit { this.patch_objects.push(x); } }); + this.patch_objects.push(this.config.object); this.stage = 2; }, complete: () => objSubscription.unsubscribe() diff --git a/app/src/app/components/stix/descriptive-property/descriptive-view/descriptive-view.component.ts b/app/src/app/components/stix/descriptive-property/descriptive-view/descriptive-view.component.ts index d5364ce6..ec95b5a1 100644 --- a/app/src/app/components/stix/descriptive-property/descriptive-view/descriptive-view.component.ts +++ b/app/src/app/components/stix/descriptive-property/descriptive-view/descriptive-view.component.ts @@ -18,6 +18,7 @@ export class DescriptiveViewComponent implements OnInit { private reReference = /\(Citation: (.*?)\)/gmu; private reLinkById = /\(LinkById: (.*?)\)/gmu; + private notFound = '[linked object not found]'; private objectLookup = {}; private sub: Subscription = new Subscription(); // prevent async issues private parseReferences = true; @@ -107,12 +108,14 @@ export class DescriptiveViewComponent implements OnInit { map((results: any) => { let data = results.data as StixObject[]; // store retrieved objects in dictionary for quick lookup - data.forEach(obj => { - // objects must be validated in cases where more than one object is - // returned by the given ATT&CK ID, this occurs due to older versions - // of ATT&CK in which techniques shared their IDs with mitigations - if (obj.isValidAttackId()) this.objectLookup[obj.attackID] = obj; - }); + if (data?.length > 0) { + data.forEach(obj => { + // objects must be validated in cases where more than one object is + // returned by the given ATT&CK ID, this occurs due to older versions + // of ATT&CK in which techniques shared their IDs with mitigations + if (obj.isValidAttackId()) this.objectLookup[obj.attackID] = obj; + }); + } return results; }) ); @@ -124,12 +127,19 @@ export class DescriptiveViewComponent implements OnInit { private replaceLinkByIds(displayStr: string, linkedIDs: string[]): string { for (let id of linkedIDs) { let obj = this.objectLookup[id]; - if (obj?.name) { - let rep = `(LinkById: ${obj.attackID})`; - let target = this.config.mode == 'edit' ? ` target="_blank"` : ``; // open linked object in new tab when editing - let linkHTML = `${obj.name}`; - displayStr = displayStr.replace(rep, linkHTML); - } + + let rep = `(LinkById: ${id})`; + + let linkHTML; + if (obj?.name) { + let url = `${obj.attackType}/${obj.stixID}`; + let target = this.config.mode == 'edit' ? ` target="_blank"` : ``; // open linked object in new tab when editing + linkHTML = `${obj.name}` + } else { + linkHTML = this.notFound; + } + let newStr = `${linkHTML}`; + displayStr = displayStr.replace(rep, newStr); } return displayStr; } @@ -168,6 +178,7 @@ export class DescriptiveViewComponent implements OnInit { // Check for LinkById tags let linkedIDs = this.getLinkedIds(this.preview); + linkedIDs = linkedIDs.filter(id => id != ''); if (!linkedIDs) { this.loading = false; return; @@ -180,13 +191,13 @@ export class DescriptiveViewComponent implements OnInit { this.loading = false; } else { let missing = linkedIDs.filter(id => Object.keys(this.objectLookup).indexOf(id) < 0); - this.sub = this.loadLinkedObjects(missing).subscribe({ - next: (results: any) => { - this.preview = this.replaceLinkByIds(this.preview, linkedIDs); - this.loading = false; - }, - complete: () => { this.sub.unsubscribe(); } - }) + this.sub = this.loadLinkedObjects(missing).subscribe({ + next: (results: any) => { + this.preview = this.replaceLinkByIds(this.preview, linkedIDs); + this.loading = false; + }, + complete: () => { this.sub.unsubscribe(); } + }); } } }