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(); }
+ });
}
}
}