@mitre-attack/attack-data-model
Version:
A TypeScript API for the MITRE ATT&CK data model
269 lines (262 loc) • 10 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/schemas/common/misc.ts
var misc_exports = {};
__export(misc_exports, {
createAttackExternalReferencesSchema: () => createAttackExternalReferencesSchema,
extensionSchema: () => extensionSchema,
extensionsSchema: () => extensionsSchema,
externalReferenceSchema: () => externalReferenceSchema,
externalReferencesSchema: () => externalReferencesSchema,
granularMarkingSchema: () => granularMarkingSchema,
stixCreatedByRefSchema: () => stixCreatedByRefSchema
});
module.exports = __toCommonJS(misc_exports);
var import_zod4 = require("zod");
// src/schemas/common/stix-identifier.ts
var import_zod2 = require("zod");
// src/schemas/common/stix-type.ts
var import_zod = require("zod");
var stixTypeToTypeName = {
"attack-pattern": "Technique",
bundle: "StixBundle",
campaign: "Campaign",
"course-of-action": "Mitigation",
identity: "Identity",
"intrusion-set": "Group",
malware: "Malware",
tool: "Tool",
"marking-definition": "MarkingDefinition",
"x-mitre-data-component": "DataComponent",
"x-mitre-data-source": "DataSource",
"x-mitre-tactic": "Tactic",
"x-mitre-asset": "Asset",
"x-mitre-matrix": "Matrix",
"x-mitre-collection": "Collection",
relationship: "Relationship",
file: "",
// not used in ATT&CK but used in sample_refs for Malware
artifact: ""
// not used in ATT&CK but used in sample_refs for Malware
// 'observed-data': 'ObservedData', // not used in ATT&CK
// 'report': 'Report', // not used in ATT&CK
// 'threat-actor': 'ThreatActor', // not used in ATT&CK
// 'vulnerability': 'Vulnerability', // not used in ATT&CK
};
var supportedStixTypes = [
"attack-pattern",
"bundle",
"campaign",
"course-of-action",
"identity",
"intrusion-set",
"malware",
"tool",
"marking-definition",
"x-mitre-data-component",
"x-mitre-data-source",
"x-mitre-tactic",
"x-mitre-asset",
"x-mitre-matrix",
"x-mitre-collection",
"relationship",
"file",
// not used in ATT&CK but used in sample_refs for Malware
"artifact"
// not used in ATT&CK but used in sample_refs for Malware
// "indicator", // not used in ATT&CK
// "observed-data", // not used in ATT&CK
// "report", // not used in ATT&CK
// "threat-actor", // not used in ATT&CK
// "vulnerability", // not used in ATT&CK
];
var stixTypeSchema = import_zod.z.enum(supportedStixTypes, {
errorMap: (issue, ctx) => {
if (issue.code === "invalid_enum_value") {
const received = typeof ctx.data === "string" ? ctx.data : String(ctx.data);
return {
message: `Invalid STIX type '${received}'. Expected one of the supported STIX types.`
};
}
return { message: ctx.defaultError };
}
}).describe(
"The type property identifies the type of STIX Object (SDO, Relationship Object, etc). The value of the type field MUST be one of the types defined by a STIX Object (e.g., indicator)."
);
// src/schemas/common/stix-identifier.ts
var isValidUuid = (uuid) => {
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);
};
var createStixIdError = (id, errorType) => {
const parts = id.split("--");
const stixType = parts.length > 0 ? parts[0] : "";
const typeName = stixType in stixTypeToTypeName ? stixTypeToTypeName[stixType] : "STIX";
let message;
switch (errorType) {
case "format":
message = `Invalid STIX Identifier for ${typeName} object: must comply with format 'type--UUIDv4'`;
break;
case "type":
message = `Invalid STIX Identifier for ${typeName} object: contains invalid STIX type '${stixType}'`;
break;
case "uuid":
message = `Invalid STIX Identifier for ${typeName} object: contains invalid UUIDv4 format`;
break;
}
return {
code: import_zod2.z.ZodIssueCode.custom,
message,
path: ["id"]
};
};
var stixIdentifierSchema = import_zod2.z.string().refine(
(val) => {
if (typeof val !== "string") return false;
if (!val.includes("--")) return false;
const [type, uuid] = val.split("--");
const isValidType = stixTypeSchema.safeParse(type).success;
const isValidUuidValue = isValidUuid(uuid);
return isValidType && isValidUuidValue;
},
(val) => {
if (typeof val !== "string") {
return createStixIdError(String(val), "format");
}
if (!val.includes("--")) {
return createStixIdError(val, "format");
}
const [type, uuid] = val.split("--");
if (!stixTypeSchema.safeParse(type).success) {
return createStixIdError(val, "type");
}
if (!isValidUuid(uuid)) {
return createStixIdError(val, "uuid");
}
return createStixIdError(val, "format");
}
).describe(
"Represents identifiers across the CTI specifications. The format consists of the name of the top-level object being identified, followed by two dashes (--), followed by a UUIDv4."
);
function createStixIdValidator(expectedType) {
const typeName = stixTypeToTypeName[expectedType] || expectedType;
return stixIdentifierSchema.refine(
(val) => val.startsWith(`${expectedType}--`),
{
message: `Invalid STIX Identifier for ${typeName}: must start with '${expectedType}--'`,
path: ["id"]
}
);
}
// src/schemas/common/attack-id.ts
var import_zod3 = require("zod");
var stixTypeToAttackIdMapping = {
"x-mitre-tactic": "tactic",
"attack-pattern": "technique",
// Note: subtechniques are also attack-patterns, but need separate handling
"intrusion-set": "group",
malware: "software",
tool: "software",
"course-of-action": "mitigation",
"x-mitre-asset": "asset",
"x-mitre-data-source": "data-source",
campaign: "campaign"
};
var attackIdPatterns = {
tactic: /^TA\d{4}$/,
technique: /^T\d{4}$/,
subtechnique: /^T\d{4}\.\d{3}$/,
group: /^G\d{4}$/,
software: /^S\d{4}$/,
mitigation: /^M\d{4}$/,
asset: /^A\d{4}$/,
"data-source": /^DS\d{4}$/,
campaign: /^C\d{4}$/
};
// src/schemas/common/misc.ts
var externalReferenceSchema = import_zod4.z.object({
source_name: import_zod4.z.string({
required_error: "Source name is required.",
invalid_type_error: "Source name must be a string."
}),
description: import_zod4.z.string({ invalid_type_error: "Description must be a string." }).optional(),
url: import_zod4.z.string({ invalid_type_error: "URL must be a string." }).url({ message: "Invalid URL format. Please provide a valid URL." }).optional(),
external_id: import_zod4.z.string({ invalid_type_error: "External ID must be a string." }).optional()
});
var externalReferencesSchema = import_zod4.z.array(externalReferenceSchema).min(1, "At least one external reference is required when 'external_references' is defined.").describe("A list of external references which refers to non-STIX information.");
var createAttackExternalReferencesSchema = (stixType) => {
return import_zod4.z.array(externalReferenceSchema).min(1, "At least one external reference is required").refine((refs) => !!refs[0]?.external_id, {
message: "ATT&CK ID must be defined in the first external_references entry.",
path: [0, "external_id"]
}).refine(
(refs) => {
if (!refs[0]?.external_id) return true;
const format = stixTypeToAttackIdMapping[stixType];
return attackIdPatterns[format].test(refs[0].external_id);
},
{
message: `The first external_reference must match the ATT&CK ID format ${getFormatExample(stixType)}.`,
path: [0, "external_id"]
}
).describe("A list of external references with the first containing a valid ATT&CK ID");
};
function getFormatExample(stixType) {
switch (stixType) {
case "x-mitre-tactic":
return "TA####";
case "attack-pattern":
return "T#### or T####.###";
case "intrusion-set":
return "G####";
case "malware":
case "tool":
return "S####";
case "course-of-action":
return "M####";
case "x-mitre-data-source":
return "DS####";
case "x-mitre-asset":
return "A####";
case "campaign":
return "C####";
default:
return "";
}
}
var stixCreatedByRefSchema = createStixIdValidator("identity").describe(
"The created_by_ref property specifies the id property of the identity object that describes the entity that created this object. If this attribute is omitted, the source of this information is undefined. This may be used by object creators who wish to remain anonymous."
);
var granularMarkingSchema = import_zod4.z.object({
marking_ref: stixIdentifierSchema,
selectors: import_zod4.z.array(import_zod4.z.string())
});
var extensionSchema = import_zod4.z.object({
extension_type: import_zod4.z.string(),
extension_properties: import_zod4.z.record(import_zod4.z.unknown())
});
var extensionsSchema = import_zod4.z.record(import_zod4.z.union([extensionSchema, import_zod4.z.record(import_zod4.z.unknown())])).describe("Specifies any extensions of the object, as a dictionary.");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createAttackExternalReferencesSchema,
extensionSchema,
extensionsSchema,
externalReferenceSchema,
externalReferencesSchema,
granularMarkingSchema,
stixCreatedByRefSchema
});