UNPKG

@mitre-attack/attack-data-model

Version:

A TypeScript API for the MITRE ATT&CK data model

204 lines (198 loc) 8.08 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/schemas/common/property-schemas/stix-external-references.ts var stix_external_references_exports = {}; __export(stix_external_references_exports, { createAttackExternalReferencesSchema: () => createAttackExternalReferencesSchema, externalReferenceSchema: () => externalReferenceSchema, externalReferencesSchema: () => externalReferencesSchema }); module.exports = __toCommonJS(stix_external_references_exports); var import_zod2 = __toESM(require("zod"), 1); // src/schemas/common/property-schemas/attack-id.ts var import_v4 = require("zod/v4"); // src/schemas/common/property-schemas/generics.ts var import_zod = require("zod"); var nonEmptyRequiredString = import_zod.z.string().trim().min(1, { error: "At least one character is required. Whitespace is not permissible." }); var emptyStixListErrorMessage = "Empty lists are prohibited in STIX and MUST NOT be used as a substitute for omitting the property if it is optional. The list MUST be present and MUST have at least one value."; var stixListOfString = import_zod.z.array(nonEmptyRequiredString).min(1, { error: emptyStixListErrorMessage }); // src/schemas/common/property-schemas/attack-id.ts 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 oldAttackIdRegex = /^MOB-(M|S)\d{4}$/; 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" }); // src/schemas/common/property-schemas/stix-external-references.ts var externalReferenceSchema = import_zod2.default.object({ source_name: nonEmptyRequiredString, description: nonEmptyRequiredString.optional(), url: import_zod2.default.url({ error: (issue) => issue.input === null ? "URL cannot be null" : "Invalid URL format. Please provide a valid URL" }).optional(), external_id: nonEmptyRequiredString.optional() }); var externalReferencesSchema = import_zod2.default.array(externalReferenceSchema).min(1).meta({ description: "A list of external references which refers to non-STIX information" }); var createAttackExternalReferencesSchema = (stixType) => { return import_zod2.default.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 attackIdType = stixTypeToAttackIdMapping[stixType]; if (attackIdType === "technique") { return attackIdPatterns["technique"].test(refs[0].external_id) || attackIdPatterns["subtechnique"].test(refs[0].external_id); } return attackIdPatterns[attackIdType].test(refs[0].external_id); }, { message: `The first external_reference must match the ATT&CK ID format ${getAttackIdExample(stixType)}.`, path: [0, "external_id"] } ).meta({ description: "A list of external references with the first containing a valid ATT&CK ID" }); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createAttackExternalReferencesSchema, externalReferenceSchema, externalReferencesSchema });