@mitre-attack/attack-data-model
Version:
A TypeScript API for the MITRE ATT&CK data model
176 lines (173 loc) • 5.75 kB
JavaScript
import {
nonEmptyRequiredString
} from "./chunk-KFUJRXYX.js";
// src/schemas/common/property-schemas/attack-id.ts
import { z } from "zod/v4";
var attackIdConfig = {
tactic: {
pattern: /^TA\d{4}$/,
message: "Must match ATT&CK Tactic ID format (TA####)",
example: "TA####",
stixTypes: ["x-mitre-tactic"]
},
technique: {
pattern: /^T\d{4}$/,
message: "Must match ATT&CK Technique ID format (T####)",
example: "T####",
stixTypes: ["attack-pattern"]
// Note: attack-pattern can be technique or subtechnique
},
subtechnique: {
pattern: /^T\d{4}\.\d{3}$/,
message: "Must match ATT&CK Sub-technique ID format (T####.###)",
example: "T####.###",
stixTypes: ["attack-pattern"]
// Note: attack-pattern can be technique or subtechnique
},
group: {
pattern: /^G\d{4}$/,
message: "Must match ATT&CK Group ID format (G####)",
example: "G####",
stixTypes: ["intrusion-set"]
},
software: {
pattern: /^S\d{4}$/,
message: "Must match ATT&CK Software ID format (S####)",
example: "S####",
stixTypes: ["malware", "tool"]
},
mitigation: {
pattern: /^M\d{4}$/,
message: "Must match ATT&CK Mitigation ID format (M####)",
example: "M####",
stixTypes: ["course-of-action"]
},
asset: {
pattern: /^A\d{4}$/,
message: "Must match ATT&CK Asset ID format (A####)",
example: "A####",
stixTypes: ["x-mitre-asset"]
},
"data-source": {
pattern: /^DS\d{4}$/,
message: "Must match ATT&CK Data Source ID format (DS####)",
example: "DS####",
stixTypes: ["x-mitre-data-source"]
},
campaign: {
pattern: /^C\d{4}$/,
message: "Must match ATT&CK Campaign ID format (C####)",
example: "C####",
stixTypes: ["campaign"]
},
"data-component": {
pattern: /^DC\d{4}$/,
message: "Must match ATT&CK Data Component Source ID format (DC####)",
example: "DC####",
stixTypes: ["x-mitre-data-component"]
},
"detection-strategy": {
pattern: /^DET\d{4}$/,
message: "Must match ATT&CK Detection Strategy Source ID format (DET####)",
example: "DET####",
stixTypes: ["x-mitre-detection-strategy"]
},
analytic: {
pattern: /^AN\d{4}$/,
message: "Must match ATT&CK Analytic Source ID format (AN####)",
example: "AN####",
stixTypes: ["x-mitre-analytic"]
}
};
var stixTypeToAttackIdMapping = {
"x-mitre-tactic": "tactic",
"attack-pattern": "technique",
// Default to technique; subtechnique handling is done contextually
"intrusion-set": "group",
malware: "software",
tool: "software",
"course-of-action": "mitigation",
"x-mitre-asset": "asset",
"x-mitre-data-source": "data-source",
campaign: "campaign",
"x-mitre-data-component": "data-component",
"x-mitre-detection-strategy": "detection-strategy",
"x-mitre-analytic": "analytic"
};
var attackIdPatterns = Object.fromEntries(
Object.entries(attackIdConfig).map(([key, config]) => [key, config.pattern])
);
var attackIdMessages = Object.fromEntries(
Object.entries(attackIdConfig).map(([key, config]) => [key, config.message])
);
var attackIdExamples = Object.fromEntries(
Object.entries(attackIdConfig).map(([key, config]) => [key, config.example])
);
function getAttackIdExample(stixType) {
if (stixType === "attack-pattern") {
return `${attackIdExamples.technique} or ${attackIdExamples.subtechnique}`;
}
const attackIdType = stixTypeToAttackIdMapping[stixType];
return attackIdExamples[attackIdType];
}
var createAttackIdSchema = (stixType) => {
const attackIdType = stixTypeToAttackIdMapping[stixType];
if (stixType === "attack-pattern") {
return z.string().refine(
(id) => attackIdPatterns.technique.test(id) || attackIdPatterns.subtechnique.test(id),
{
error: `Must match either ATT&CK Technique ID format (${attackIdExamples.technique}) or Sub-technique ID format (${attackIdExamples.subtechnique})`
}
);
}
return z.string().regex(attackIdPatterns[attackIdType], attackIdMessages[attackIdType]).meta({
description: "ATT&CK IDs are human-readable identifiers commonly used for referencing objects in documentation and communication. Each ATT&CK object type follows a specific ID format, typically denoted by a type code prefix (e.g., `TA` for tactic) followed by a four-digit identifier (e.g., 0001)"
});
};
var oldAttackIdRegex = /^MOB-(M|S)\d{4}$/;
function createOldMitreAttackIdSchema(stixType) {
const baseSchema = nonEmptyRequiredString.meta({
description: "Old ATT&CK IDs that may have been associated with this object"
});
switch (stixType) {
case "malware":
case "tool":
return baseSchema.refine(
(value) => {
return /^MOB-S\d{4}$/.test(value);
},
{
message: `x_mitre_old_attack_id for ${stixType} need to be in the format MOB-S####`
}
);
case "course-of-action":
return baseSchema.refine(
(value) => {
return /^MOB-M\d{4}$/.test(value);
},
{
message: `x_mitre_old_attack_id for ${stixType} need to be in the format MOB-M####`
}
);
default:
throw new Error(`Unsupported STIX type: ${stixType}`);
}
}
var xMitreOldAttackIdSchema = nonEmptyRequiredString.refine(
(value) => {
return oldAttackIdRegex.test(value);
},
{
message: "Must be in the format 'MOB-X0000' where X is either 'M' or 'S', followed by exactly four digits"
}
).meta({ description: "Old ATT&CK IDs that may have been associated with this object" });
export {
stixTypeToAttackIdMapping,
attackIdPatterns,
attackIdMessages,
attackIdExamples,
getAttackIdExample,
createAttackIdSchema,
createOldMitreAttackIdSchema,
xMitreOldAttackIdSchema
};