UNPKG

@mitre-attack/attack-data-model

Version:

A TypeScript API for the MITRE ATT&CK data model

1,354 lines (1,310 loc) 130 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/index.ts var index_exports = {}; __export(index_exports, { ATTACK_SPEC_VERSION: () => ATTACK_SPEC_VERSION, AnalyticImpl: () => AnalyticImpl, AssetImpl: () => AssetImpl, AttackBaseImpl: () => AttackBaseImpl, AttackDataModel: () => AttackDataModel, AttackMotivationOV: () => AttackMotivationOV, AttackResourceLevelOV: () => AttackResourceLevelOV, CampaignImpl: () => CampaignImpl2, CollectionImpl: () => CollectionImpl, DataComponentImpl: () => DataComponentImpl, DataSourceImpl: () => DataSourceImpl, DataSourceRegistration: () => DataSourceRegistration, DetectionStrategyImpl: () => DetectionStrategyImpl, GroupImpl: () => GroupImpl, IdentityClassOV: () => IdentityClassOV, IdentityImpl: () => IdentityImpl, ImplementationLanguageOV: () => ImplementationLanguageOV, IndicatorTypeOV: () => IndicatorTypeOV, IndustrySectorOV: () => IndustrySectorOV, MalwareCapabilityOV: () => MalwareCapabilityOV, MalwareImpl: () => MalwareImpl, MalwareTypeOV: () => MalwareTypeOV, MarkingDefinitionImpl: () => MarkingDefinitionImpl, MatrixImpl: () => MatrixImpl, MitigationImpl: () => MitigationImpl, MitreCollectionLayerOV: () => MitreCollectionLayerOV, PatternTypeOV: () => PatternTypeOV, ProcessorArchitectureOV: () => ProcessorArchitectureOV, TacticImpl: () => TacticImpl, TechniqueImpl: () => TechniqueImpl2, ToolImpl: () => ToolImpl, ToolTypeOV: () => ToolTypeOV, aliasesSchema: () => aliasesSchema, analyticSchema: () => analyticSchema, assetSchema: () => assetSchema, attackBaseDomainObjectSchema: () => attackBaseDomainObjectSchema, attackBaseMetaObjectSchema: () => attackBaseMetaObjectSchema, attackBaseRelationshipObjectSchema: () => attackBaseRelationshipObjectSchema, attackDomainSchema: () => attackDomainSchema, attackIdExamples: () => attackIdExamples, attackIdMessages: () => attackIdMessages, attackIdPatterns: () => attackIdPatterns, attackObjectsSchema: () => attackObjectsSchema, baseMarkingDefinitionSchema: () => baseMarkingDefinitionSchema, campaignSchema: () => campaignSchema, collectionSchema: () => collectionSchema, createAttackExternalReferencesSchema: () => createAttackExternalReferencesSchema, createAttackIdInExternalReferencesRefinement: () => createAttackIdInExternalReferencesRefinement, createAttackIdSchema: () => createAttackIdSchema, createCitationsRefinement: () => createCitationsRefinement, createEnterpriseOnlyPropertiesRefinement: () => createEnterpriseOnlyPropertiesRefinement, createFirstAliasRefinement: () => createFirstAliasRefinement, createFirstBundleObjectRefinement: () => createFirstBundleObjectRefinement, createFirstXMitreAliasRefinement: () => createFirstXMitreAliasRefinement, createMobileOnlyPropertiesRefinement: () => createMobileOnlyPropertiesRefinement, createMultiStixTypeValidator: () => createMultiStixTypeValidator, createOldMitreAttackIdSchema: () => createOldMitreAttackIdSchema, createRelationshipValidationRefinement: () => createRelationshipValidationRefinement, createStixIdValidator: () => createStixIdValidator, createStixTypeValidator: () => createStixTypeValidator, dataComponentSchema: () => dataComponentSchema, dataSourceSchema: () => dataSourceSchema, descriptionSchema: () => descriptionSchema, detectionStrategySchema: () => detectionStrategySchema, extensionDefinitionSchema: () => extensionDefinitionSchema, extensionObjectTypeSchema: () => extensionObjectTypeSchema, extensionSchema: () => extensionSchema, extensionTypeSchema: () => extensionTypeSchema, extensionsSchema: () => extensionsSchema, externalReferenceSchema: () => externalReferenceSchema, externalReferencesSchema: () => externalReferencesSchema, fetchAttackVersions: () => fetchAttackVersions, getAttackIdExample: () => getAttackIdExample, getDataSources: () => getDataSources, getMitigations: () => getMitigations, getPlatforms: () => getPlatforms, getSubTechniques: () => getSubTechniques, getTactics: () => getTactics, granularMarkingSchema: () => granularMarkingSchema, groupSchema: () => groupSchema, identitySchema: () => identitySchema, invalidRelationships: () => invalidRelationships, isValidRelationship: () => isValidRelationship, killChainNameSchema: () => killChainNameSchema, killChainPhaseSchema: () => killChainPhaseSchema, loadDataModel: () => loadDataModel, malwareSchema: () => malwareSchema, markingDefinitionSchema: () => markingDefinitionSchema, matrixSchema: () => matrixSchema, mitigationSchema: () => mitigationSchema, nameSchema: () => nameSchema, nonEmptyRequiredString: () => nonEmptyRequiredString, objectMarkingRefsSchema: () => objectMarkingRefsSchema, objectVersionReferenceSchema: () => objectVersionReferenceSchema, registerDataSource: () => registerDataSource, relatedAssetSchema: () => relatedAssetSchema, relatedAssetsSchema: () => relatedAssetsSchema, relationshipSchema: () => relationshipSchema, relationshipTypeSchema: () => relationshipTypeSchema, softwareSchema: () => softwareSchema, statementMarkingObjectSchema: () => statementMarkingObjectSchema, stixBundleSchema: () => stixBundleSchema, stixCreatedByRefSchema: () => stixCreatedByRefSchema, stixCreatedTimestampSchema: () => stixCreatedTimestampSchema, stixDomainObjectSchema: () => stixDomainObjectSchema, stixIdentifierSchema: () => stixIdentifierSchema, stixListOfString: () => stixListOfString, stixModifiedTimestampSchema: () => stixModifiedTimestampSchema, stixRelationshipObjectSchema: () => stixRelationshipObjectSchema, stixSpecVersionSchema: () => stixSpecVersionSchema, stixTimestampSchema: () => stixTimestampSchema, stixTypeSchema: () => stixTypeSchema, stixTypeToAttackIdMapping: () => stixTypeToAttackIdMapping, stixTypeToTypeName: () => stixTypeToTypeName, tacticSchema: () => tacticSchema, techniqueSchema: () => techniqueSchema, tlpAmberSchema: () => tlpAmberSchema, tlpGreenSchema: () => tlpGreenSchema, tlpMarkingDefinitionSchema: () => tlpMarkingDefinitionSchema, tlpMarkingObjectSchema: () => tlpMarkingObjectSchema, tlpRedSchema: () => tlpRedSchema, tlpWhiteSchema: () => tlpWhiteSchema, toolSchema: () => toolSchema, validRelationshipObjectTypes: () => validRelationshipObjectTypes, xMitreAttackSpecVersionSchema: () => xMitreAttackSpecVersionSchema, xMitreCollectionLayersSchema: () => xMitreCollectionLayersSchema, xMitreContributorsSchema: () => xMitreContributorsSchema, xMitreDataSourceRefSchema: () => xMitreDataSourceRefSchema, xMitreDataSourceSchema: () => xMitreDataSourceSchema, xMitreDataSourcesSchema: () => xMitreDataSourcesSchema, xMitreDefenseBypassesSchema: () => xMitreDefenseBypassesSchema, xMitreDeprecatedSchema: () => xMitreDeprecatedSchema, xMitreDetectionSchema: () => xMitreDetectionSchema, xMitreDomainsSchema: () => xMitreDomainsSchema, xMitreEffectivePermissionsSchema: () => xMitreEffectivePermissionsSchema, xMitreFirstSeenCitationSchema: () => xMitreFirstSeenCitationSchema, xMitreIdentity: () => xMitreIdentity, xMitreIdentitySchema: () => xMitreIdentitySchema, xMitreImpactTypeSchema: () => xMitreImpactTypeSchema, xMitreIsSubtechniqueSchema: () => xMitreIsSubtechniqueSchema, xMitreLastSeenCitationSchema: () => xMitreLastSeenCitationSchema, xMitreLogSourcesSchema: () => xMitreLogSourcesSchema, xMitreModifiedByRefSchema: () => xMitreModifiedByRefSchema, xMitreNetworkRequirementsSchema: () => xMitreNetworkRequirementsSchema, xMitreOldAttackIdSchema: () => xMitreOldAttackIdSchema, xMitrePermissionsRequiredSchema: () => xMitrePermissionsRequiredSchema, xMitrePlatformSchema: () => xMitrePlatformSchema, xMitrePlatformsSchema: () => xMitrePlatformsSchema, xMitreRemoteSupportSchema: () => xMitreRemoteSupportSchema, xMitreSectorsSchema: () => xMitreSectorsSchema, xMitreShortNameSchema: () => xMitreShortNameSchema, xMitreSystemRequirementsSchema: () => xMitreSystemRequirementsSchema, xMitreTacticRefsSchema: () => xMitreTacticRefsSchema, xMitreTacticTypeSchema: () => xMitreTacticTypeSchema, xMitreVersionSchema: () => xMitreVersionSchema }); module.exports = __toCommonJS(index_exports); // src/attack-spec-version.ts var ATTACK_SPEC_VERSION = "3.3.0"; // src/classes/common/attack-object.impl.ts var AttackBaseImpl = class { /** * Sets the object that revokes the current object. * @param obj - The object that revokes this object. */ setRevokedBy(obj) { this.revokedBy = obj; } /** * Returns the object that revoked this object. */ getRevokedBy() { return this.revokedBy; } }; // src/classes/sdo/asset.impl.ts var AssetImpl = class extends AttackBaseImpl { constructor(asset) { super(); this.asset = asset; // Custom properties prefixed with `_` to avoid conflicts this._techniques = []; Object.assign(this, asset); } addTechnique(technique) { this._techniques.push(technique); } // Custom method or property getDisplayName() { return `${this.asset.name} - Asset`; } }; // src/classes/sdo/data-component.impl.ts var DataComponentImpl = class extends AttackBaseImpl { constructor(dataComponent) { super(); this.dataComponent = dataComponent; this._detectedTechniques = []; Object.assign(this, dataComponent); } // Add a technique detected by this data component addDetectedTechnique(technique) { this._detectedTechniques.push(technique); } // Getters getDetectedTechniques() { return this._detectedTechniques; } }; // src/classes/sdo/mitigation.impl.ts var MitigationImpl = class extends AttackBaseImpl { constructor(mitigation) { super(); this.mitigation = mitigation; Object.assign(this, mitigation); } }; // src/classes/sdo/tactic.impl.ts var TacticImpl = class extends AttackBaseImpl { constructor(tactic) { super(); this.tactic = tactic; Object.assign(this, tactic); } }; // src/classes/sdo/technique.impl.ts var TechniqueImpl2 = class extends AttackBaseImpl { constructor(technique) { super(); this.technique = technique; this._subTechniques = []; this._tactics = []; this._mitigations = []; this._relatedTechniques = []; this._targetAssets = []; this._detectingDataComponents = []; Object.assign(this, technique); } setParent(parent) { this._parentTechnique = parent; } addSubTechnique(subTechnique) { this._subTechniques.push(subTechnique); } addTactic(tactic) { this._tactics.push(tactic); } addMitigation(mitigation) { this._mitigations.push(mitigation); } addRelatedTechnique(technique) { this._relatedTechniques.push(technique); } addTargetAsset(asset) { this._targetAssets.push(asset); } addDetectingDataComponent(dataComponent) { this._detectingDataComponents.push(dataComponent); } // Getters getSubTechniques() { return this._subTechniques; } getTactics() { return this._tactics; } getMitigations() { return this._mitigations; } getParentTechnique() { return this._parentTechnique; } getRelatedTechniques() { return this._relatedTechniques; } getTargetAssets() { return this._targetAssets; } getDetectingDataComponents() { return this._detectingDataComponents; } }; // src/classes/sdo/malware.impl.ts var MalwareImpl = class extends AttackBaseImpl { constructor(malware) { super(); this.malware = malware; this._techniques = []; Object.assign(this, malware); } // Add a technique used by the malware addTechnique(technique) { this._techniques.push(technique); } // Getters getTechniques() { return this._techniques; } }; // src/classes/sdo/tool.impl.ts var ToolImpl = class extends AttackBaseImpl { constructor(tool) { super(); this.tool = tool; this._techniques = []; Object.assign(this, tool); } // Add a technique used by the tool addTechnique(technique) { this._techniques.push(technique); } // Getters getTechniques() { return this._techniques; } }; // src/classes/sdo/group.impl.ts var GroupImpl = class extends AttackBaseImpl { constructor(group) { super(); this.group = group; this._techniques = []; this._software = []; this._attributedCampaigns = []; Object.assign(this, group); } // Add a technique used by the group addTechnique(technique) { this._techniques.push(technique); } // Add software used by the group addSoftware(software) { this._software.push(software); } addAttributedCampaign(campaign) { this._attributedCampaigns.push(campaign); } // Getters getTechniques() { return this._techniques; } getSoftware() { return this._software; } getAttributedCampaigns() { return this._attributedCampaigns; } }; // src/classes/sdo/campaign.impl.ts var CampaignImpl2 = class extends AttackBaseImpl { constructor(campaign) { super(); this.campaign = campaign; this._techniques = []; this._software = []; Object.assign(this, campaign); } // Add a technique used by the campaign addTechnique(technique) { this._techniques.push(technique); } // Add software used by the campaign addSoftware(software) { this._software.push(software); } // Set the group this campaign is attributed to setAttributedTo(group) { this._attributedTo = group; } // Getters getTechniques() { return this._techniques; } getSoftware() { return this._software; } getAttributedTo() { return this._attributedTo; } }; // src/classes/sdo/collection.impl.ts var CollectionImpl = class extends AttackBaseImpl { constructor(collection) { super(); this.collection = collection; Object.assign(this, collection); } }; // src/classes/sdo/data-source.impl.ts var DataSourceImpl = class extends AttackBaseImpl { constructor(dataSource) { super(); this.dataSource = dataSource; Object.assign(this, dataSource); } }; // src/classes/sdo/identity.impl.ts var IdentityImpl = class extends AttackBaseImpl { constructor(identity) { super(); this.identity = identity; Object.assign(this, identity); } }; // src/classes/sdo/matrix.impl.ts var MatrixImpl = class extends AttackBaseImpl { constructor(matrix) { super(); this.matrix = matrix; Object.assign(this, matrix); } }; // src/classes/sdo/detection-strategy.impl.ts var DetectionStrategyImpl = class extends AttackBaseImpl { constructor(detectionStrategy) { super(); this.detectionStrategy = detectionStrategy; this._techniques = []; Object.assign(this, detectionStrategy); } // Add a technique used by the group addTechnique(technique) { this._techniques.push(technique); } // Getters getTechniques() { return this._techniques; } get detects() { return this._techniques; } }; // src/classes/sdo/analytic.impl.ts var AnalyticImpl = class extends AttackBaseImpl { constructor(analytic) { super(); this.analytic = analytic; Object.assign(this, analytic); } }; // src/classes/smo/marking-definition.impl.ts var MarkingDefinitionImpl = class extends AttackBaseImpl { constructor(markingDefinition) { super(); this.markingDefinition = markingDefinition; Object.assign(this, markingDefinition); } }; // src/classes/sro/relationship.impl.ts var RelationshipImpl = class extends AttackBaseImpl { constructor(relationship) { super(); this.relationship = relationship; Object.assign(this, relationship); } }; // src/classes/attack-data-model.ts var AttackDataModel = class { constructor(uuid, attackObjects) { this.uuid = uuid; this.attackObjects = attackObjects; this.techniques = []; this.campaigns = []; this.mitigations = []; this.identities = []; this.groups = []; this.malware = []; this.tools = []; this.markingDefinitions = []; this.dataComponents = []; this.dataSources = []; this.tactics = []; this.assets = []; this.matrices = []; this.collections = []; this.relationships = []; this.detectionStrategies = []; this.analytics = []; this.populateData(); } /** * Returns the unique identifier for this data source/model. * @returns string - Returns the unique identifier for this data source/model */ getUuid() { return this.uuid; } /** * Returns a list of ATT&CK objects that have been parsed by Zod schemas. These objects are not TS classes, but are plain JS objects. They do not contain relationship mappings. * @returns AttackObject[] - a list of ATT&CK objects that have been parsed by Zod schemas. These objects are not TS classes, but are plain JS objects. They do not contain relationship mappings. */ getAttackObjects() { return this.attackObjects; } /** * Populates the class properties (e.g., techniques, groups, etc.) from the parsed objects array. */ populateData() { const objectMap = /* @__PURE__ */ new Map(); this.attackObjects.forEach((object) => { switch (object.type) { // ASSET case "x-mitre-asset": { const asset = new AssetImpl(object); this.assets.push(asset); objectMap.set(object.id, asset); break; } // CAMPAIGN case "campaign": { const campaign = new CampaignImpl2(object); this.campaigns.push(campaign); objectMap.set(object.id, campaign); break; } // COLLECTION case "x-mitre-collection": { const collection = new CollectionImpl(object); this.collections.push(collection); objectMap.set(object.id, collection); break; } // DATA COMPONENT case "x-mitre-data-component": { const dataComponent = new DataComponentImpl(object); this.dataComponents.push(dataComponent); objectMap.set(object.id, dataComponent); break; } // DATA SOURCE case "x-mitre-data-source": { const dataSource = new DataSourceImpl(object); this.dataSources.push(dataSource); objectMap.set(object.id, dataSource); break; } // GROUP case "intrusion-set": { const group = new GroupImpl(object); this.groups.push(group); objectMap.set(object.id, group); break; } // IDENTITY case "identity": { const identity = new IdentityImpl(object); this.identities.push(identity); objectMap.set(object.id, identity); break; } // MALWARE case "malware": { const malware = new MalwareImpl(object); this.malware.push(malware); objectMap.set(object.id, malware); break; } // MATRIX case "x-mitre-matrix": { const matrix = new MatrixImpl(object); this.matrices.push(matrix); objectMap.set(object.id, matrix); break; } // MITIGATION case "course-of-action": { const mitigation = new MitigationImpl(object); this.mitigations.push(mitigation); objectMap.set(object.id, mitigation); break; } // TACTIC case "x-mitre-tactic": { const tactic = new TacticImpl(object); this.tactics.push(tactic); objectMap.set(object.id, tactic); break; } // TECHNIQUE case "attack-pattern": { const technique = new TechniqueImpl2(object); this.techniques.push(technique); objectMap.set(object.id, technique); break; } // TOOL case "tool": { const tool = new ToolImpl(object); this.tools.push(tool); objectMap.set(object.id, tool); break; } // MARKING DEFINITION case "marking-definition": { const markingDefinition = new MarkingDefinitionImpl(object); this.markingDefinitions.push(markingDefinition); objectMap.set(object.id, markingDefinition); break; } // RELATIONSHIP case "relationship": { const relationship = new RelationshipImpl(object); this.relationships.push(relationship); objectMap.set(object.id, relationship); break; } // DETECTION STRATEGY case "x-mitre-detection-strategy": { const detectionStrategy = new DetectionStrategyImpl(object); this.detectionStrategies.push(detectionStrategy); objectMap.set(object.id, detectionStrategy); break; } // ANALYTIC case "x-mitre-analytic": { const analytic = new AnalyticImpl(object); this.analytics.push(analytic); objectMap.set(object.id, analytic); break; } } }); this.initializeRelationships(objectMap); } /** * Initializes relationships between objects, such as sub-techniques, tactics, mitigations, and more. */ initializeRelationships(objectMap) { this.relationships.forEach((relationship) => { const sourceObj = objectMap.get(relationship.source_ref); const targetObj = objectMap.get(relationship.target_ref); if (sourceObj && targetObj) { switch (relationship.relationship_type) { case "subtechnique-of": if (sourceObj instanceof TechniqueImpl2 && targetObj instanceof TechniqueImpl2) { sourceObj.setParent(targetObj); targetObj.addSubTechnique(sourceObj); } break; case "uses": if (sourceObj instanceof GroupImpl && targetObj instanceof TechniqueImpl2) { sourceObj.addTechnique(targetObj); } else if (sourceObj instanceof CampaignImpl2 && targetObj instanceof TechniqueImpl2) { sourceObj.addTechnique(targetObj); } else if (sourceObj instanceof MalwareImpl && targetObj instanceof TechniqueImpl2) { sourceObj.addTechnique(targetObj); } else if (sourceObj instanceof ToolImpl && targetObj instanceof TechniqueImpl2) { sourceObj.addTechnique(targetObj); } else if (sourceObj instanceof GroupImpl && (targetObj instanceof MalwareImpl || targetObj instanceof ToolImpl)) { sourceObj.addSoftware(targetObj); } else if (sourceObj instanceof CampaignImpl2 && (targetObj instanceof MalwareImpl || targetObj instanceof ToolImpl)) { sourceObj.addSoftware(targetObj); } break; case "mitigates": if (sourceObj instanceof MitigationImpl && targetObj instanceof TechniqueImpl2) { targetObj.addMitigation(sourceObj); } break; case "detects": if (sourceObj instanceof DataComponentImpl && targetObj instanceof TechniqueImpl2) { sourceObj.addDetectedTechnique(targetObj); targetObj.addDetectingDataComponent(sourceObj); } break; case "targets": if (sourceObj instanceof TechniqueImpl2 && targetObj instanceof AssetImpl) { sourceObj.addTargetAsset(targetObj); } break; case "attributed-to": if (sourceObj instanceof CampaignImpl2 && targetObj instanceof GroupImpl) { sourceObj.setAttributedTo(targetObj); targetObj.addAttributedCampaign(sourceObj); } break; case "revoked-by": if (sourceObj.constructor.name === targetObj.constructor.name) { sourceObj.setRevokedBy(targetObj); } break; default: break; } } }); } // Other methods to query objects, get by ID, etc. (unchanged from previous version) }; // src/classes/utils.ts function getSubTechniques(technique, relationships, attackObjects) { return relationships.filter((rel) => rel.relationship_type === "subtechnique-of" && rel.source_ref === technique.id).map((rel) => { const subTech = attackObjects.find((obj) => obj.id === rel.target_ref); return subTech; }).filter((subTech) => subTech !== void 0); } function getTactics(technique, relationships, attackObjects) { const killChainPhaseIds = technique.kill_chain_phases?.map((phase) => phase.phase_name) ?? []; return attackObjects.filter((obj) => obj.type === "x-mitre-tactic" && killChainPhaseIds.includes(obj.id)).map((obj) => new TacticImpl(obj)); } function getPlatforms(technique) { return technique.x_mitre_platforms ?? []; } function getMitigations(technique, relationships, attackObjects) { return relationships.filter((rel) => rel.relationship_type === "mitigates" && rel.target_ref === technique.id).map((rel) => { const mitigation = attackObjects.find( (obj) => obj.id === rel.source_ref && obj.type === "course-of-action" ); if (mitigation) { return new MitigationImpl(mitigation); } return null; }).filter((mitigation) => mitigation !== null); } function getDataSources(technique, relationships, attackObjects) { return relationships.filter((rel) => rel.relationship_type === "detects" && rel.target_ref === technique.id).map((rel) => { const dataSource = attackObjects.find( (obj) => obj.id === rel.source_ref && obj.type === "x-mitre-data-source" ); if (dataSource) { return new DataSourceImpl(dataSource); } return null; }).filter((dataSource) => dataSource !== null); } // src/data-sources/fetch-attack-versions.ts function normalizeVersion(version) { return version.replace(/^v/, ""); } async function fetchAttackVersions() { const url = "https://api.github.com/repos/mitre-attack/attack-stix-data/releases"; const response = await fetch(url, { headers: { Accept: "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const releases = await response.json(); const versions = releases.map((release) => normalizeVersion(release.tag_name)).sort((a, b) => { const [aMajor, aMinor] = a.split(".").map(Number); const [bMajor, bMinor] = b.split(".").map(Number); if (bMajor !== aMajor) return bMajor - aMajor; return bMinor - aMinor; }); return versions; } // src/data-sources/data-source-registration.ts var DataSourceRegistration = class { /** * Creates a new DataSourceRegistration instance. * @param options - The data source options to register. */ constructor(options) { this.options = options; this.validateOptions(); } /** * Validates the data source options to ensure the correct fields are provided for each source type. * @throws An error if validation fails. */ async validateOptions() { const { source, parsingMode } = this.options; if (parsingMode && !["strict", "relaxed"].includes(parsingMode)) { throw new Error(`Invalid parsingMode: ${parsingMode}. Expected 'strict' or 'relaxed'.`); } switch (source) { case "attack": { await this.validateAttackOptions(); break; } case "file": { this.validateFileOptions(); break; } case "url": case "taxii": { throw new Error(`The ${source} source is not implemented yet.`); } default: { throw new Error(`Unsupported data source type: ${source}`); } } } /** * Validates options specific to the 'attack' source type. * @throws An error if validation fails. */ async validateAttackOptions() { const { domain, version } = this.options; if (!domain || !Object.values(attackDomainSchema.enum).includes(domain)) { throw new Error( `Invalid domain provided for 'attack' source. Expected one of: ${Object.values( attackDomainSchema.enum ).join(", ")}` ); } if (version) { const supportedVersions = await fetchAttackVersions(); const normalizedVersion = version.replace(/^v/, ""); if (!supportedVersions.includes(normalizedVersion)) { throw new Error( `Invalid version: ${version}. Supported versions are: ${supportedVersions.join(", ")}` ); } } } /** * Validates options specific to the 'file' source type. * @throws An error if validation fails. */ validateFileOptions() { const { path } = this.options; if (!path) { throw new Error("The 'file' source requires a 'path' field to specify the file location."); } } }; // src/main.ts var import_axios = __toESM(require("axios"), 1); var import_uuid = require("uuid"); // src/schemas/sdo/stix-bundle.schema.ts var import_v434 = require("zod/v4"); // src/refinements/index.ts var import_v433 = require("zod/v4"); // src/schemas/common/attack-core.ts var import_v414 = 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 stixListOfString = import_zod.z.array(nonEmptyRequiredString).min(1, { error: "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." }); // src/schemas/common/property-schemas/attack-attribution.ts var import_v43 = require("zod/v4"); // src/schemas/common/property-schemas/stix-id.ts var import_v42 = require("zod/v4"); // src/schemas/common/property-schemas/stix-type.ts var import_v4 = require("zod/v4"); var stixTypeToTypeName = { "attack-pattern": "Technique", bundle: "StixBundle", campaign: "Campaign", "course-of-action": "Mitigation", "extension-definition": null, identity: "Identity", "intrusion-set": "Group", malware: "Malware", tool: "Tool", "marking-definition": "MarkingDefinition", "x-mitre-analytic": "Analytic", "x-mitre-data-component": "DataComponent", "x-mitre-detection-strategy": "DetectionStrategy", "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", "extension-definition", "identity", "intrusion-set", "malware", "tool", "marking-definition", "x-mitre-analytic", "x-mitre-data-component", "x-mitre-detection-strategy", "x-mitre-tactic", "x-mitre-asset", "x-mitre-data-source", "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_v4.z.enum(supportedStixTypes, { error: (issue) => { if (issue.code === "invalid_value") { const received = typeof issue.input === "string" ? issue.input : String(issue.input); return `Invalid STIX type '${received}'. Expected one of the supported STIX types.`; } return void 0; } }).meta({ description: "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)." }); function createStixTypeValidator(stixType) { const objectName = stixTypeToTypeName[stixType]; return import_v4.z.literal(stixType).refine((val) => val === stixType, { error: (issue) => `Invalid 'type' property. Expected '${stixType}' for ${objectName} object, but received '${issue.input}'.` }); } function createMultiStixTypeValidator(stixTypes) { const objectNames = stixTypes.map((type) => stixTypeToTypeName[type]).join(" or "); const typeList = stixTypes.map((t) => `'${t}'`).join(" or "); const literals = stixTypes.map((type) => import_v4.z.literal(type)); return import_v4.z.union(literals).refine((val) => stixTypes.includes(val), { error: (issue) => `Invalid 'type' property. Expected ${typeList} for ${objectNames} object, but received '${issue.input}'.` }); } // src/schemas/common/property-schemas/stix-id.ts var stixIdentifierSchema = import_v42.z.string().refine((val) => val.includes("--") && val.split("--").length === 2, { error: (issue) => ({ code: "custom", message: "Invalid STIX Identifier: must comply with format 'type--UUIDv4'", input: issue.input, path: [] }) }).refine( (val) => { const [type] = val.split("--"); return stixTypeSchema.safeParse(type).success; }, { error: (issue) => { const val = issue.input; const [type] = val.split("--"); const typeName = type in stixTypeToTypeName ? stixTypeToTypeName[type] : "STIX"; return { code: "custom", message: `Invalid STIX Identifier for ${typeName} object: contains invalid STIX type '${type}'`, input: issue.input, path: [] }; } } ).refine( (val) => { const [, uuid] = val.split("--"); return import_v42.z.uuid().safeParse(uuid).success; }, { error: (issue) => { const val = issue.input; const [type] = val.split("--"); const typeName = type in stixTypeToTypeName ? stixTypeToTypeName[type] : "STIX"; return { code: "custom", message: `Invalid STIX Identifier for ${typeName} object: contains invalid UUIDv4 format`, input: issue.input, path: [] }; } } ).meta({ description: "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) { return stixIdentifierSchema.refine( (val) => val.startsWith(`${expectedType}--`), { error: () => ({ code: "custom", message: `Invalid STIX Identifier: must start with '${expectedType}--'`, input: expectedType, path: [] }) } ); } // src/schemas/common/property-schemas/attack-attribution.ts var xMitreIdentity = "identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5"; var xMitreIdentitySchema = import_v43.z.literal(xMitreIdentity).meta({ description: "STIX Identity for The MITRE Corporation" }); var xMitreModifiedByRefSchema = xMitreIdentitySchema.meta({ description: "The STIX ID of the MITRE identity object. Used to track the identity of the MITRE organization, which created the current version of the object. Previous versions of the object may have been created by other individuals or organizations." }); var xMitreContributorsSchema = stixListOfString.meta({ description: "People and organizations who have contributed to the object. Not found on objects of type `relationship`." }); // src/schemas/common/property-schemas/attack-domains.ts var import_v44 = require("zod/v4"); var attackDomainSchema = import_v44.z.enum(["enterprise-attack", "mobile-attack", "ics-attack"]).meta({ description: "ATT&CK is organized in a series of \u201Ctechnology domains\u201D - the ecosystem an adversary operates within that provides a set of constraints the adversary must circumvent or take advantage of to accomplish a set of objectives. To date MITRE has defined three technology domains." }); var xMitreDomainsSchema = import_v44.z.array(attackDomainSchema).min(1, { message: "At least one MITRE ATT&CK domain must be specified." }).meta({ description: "The technology domains to which the ATT&CK object belongs." }); // src/schemas/common/property-schemas/attack-id.ts var import_v45 = require("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 import_v45.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 import_v45.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" }); // src/schemas/common/property-schemas/attack-platforms.ts var import_zod2 = require("zod"); var supportedMitrePlatforms = [ "Field Controller/RTU/PLC/IED", "Network Devices", "Data Historian", "Google Workspace", "Office Suite", "ESXi", "Identity Provider", "Containers", "Azure AD", "Engineering Workstation", "Control Server", "Human-Machine Interface", "Windows", "Linux", "IaaS", "None", "iOS", "PRE", "SaaS", "Input/Output Server", "macOS", "Android", "Safety Instrumented System/Protection Relay", "Embedded" ]; var xMitrePlatformSchema = import_zod2.z.enum(supportedMitrePlatforms, { error: () => `Platform must be one of: ${supportedMitrePlatforms.join(", ")}` }).meta({ description: "A technology environments and/or operating system that ATT&CK techniques are applicable within." }); var xMitrePlatformsSchema = import_zod2.z.array(xMitrePlatformSchema, { error: (issue) => issue.code === "invalid_type" ? "x_mitre_platforms must be an array of strings" : "Invalid platforms array" }).min(1, "At least one platform is required").refine((items) => new Set(items).size === items.length, { message: "Platforms must be unique (no duplicates allowed)." }).meta({ description: "List of platforms that apply to the object." }); // src/schemas/common/property-schemas/attack-statuses.ts var import_zod3 = require("zod"); var xMitreDeprecatedSchema = import_zod3.z.boolean({ error: "x_mitre_deprecated must be a boolean." }).meta({ description: "Indicates whether the object has been deprecated." }); // src/schemas/common/property-schemas/attack-versioning.ts var import_v46 = require("zod/v4"); var xMitreVersionSchema = nonEmptyRequiredString.regex(/^(\d{1,2})\.(\d{1,2})$/, "Version must be in format 'M.N' where M and N are 0-99").meta({ description: "Represents the version of the object in a 'major.minor' format, where both 'major' and 'minor' are integers between 0 and 99. This versioning follows semantic versioning principles but excludes the patch number. The version number is incremented by ATT&CK when the content of the object is updated. This property does not apply to relationship objects." }); var semverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/; var xMitreAttackSpecVersionSchema = nonEmptyRequiredString.regex(semverRegex, "Must be valid semantic version (MAJOR.MINOR.PATCH)").meta({ description: "The version of the ATT&CK spec used by the object. This field helps consuming software determine if the data format is supported. If the field is not present on an object, the spec version will be assumed to be 2.0.0. Refer to the ATT&CK CHANGELOG for all supported versions." }); // src/schemas/common/property-schemas/stix-attribution.ts var import_v47 = require("zod/v4"); var objectMarkingRefsSchema = import_v47.z.array( stixIdentifierSchema.startsWith( "marking-definition--", 'Identifier must start with "marking-definition--"' ) ).meta({ description: "The list of marking-definition objects to be applied to this object." }); var stixCreatedByRefSchema = createStixIdValidator("identity").meta({ description: "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." }); // src/schemas/common/property-schemas/stix-common-properties.ts var import_v48 = require("zod/v4"); var descriptionSchema = nonEmptyRequiredString.meta({ description: "A description of the object." }); var nameSchema = nonEmptyRequiredString.meta({ description: "The name of the object." }); var aliasesSchema = stixListOfString.meta({ description: "Alternative names used to identify this object. The first alias must match the object's name." }); // src/schemas/common/property-schemas/stix-extensions.ts var import_v410 = require("zod/v4"); // src/schemas/common/property-schemas/stix-external-references.ts var import_zod4 = __toESM(require("zod"), 1); var externalReferenceSchema = import_zod4.default.object({ source_name: nonEmptyRequiredString, description: nonEmptyRequiredString.optional(), url: import_zod4.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_zod4.default.array(externalReferenceSchema).min(1).meta({ description: "A list of external references which refers to non-STIX information" }); var createAttackExternalReferencesSchema = (stixType) => { return import_zod4.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" }); }; // src/schemas/common/property-schemas/stix-granular-marking.ts var import_zod5 = require("zod"); var granularMarkingSchema = import_zod5.z.object({ lang: nonEmptyRequiredString.optional().meta({ description: "The lang property identifies the language of the text identified by this marking. The value of the lang property, if present, MUST be an [RFC5646] language code. If the marking_ref property is not present, this property MUST be present. If the marking_ref property is present, this property MUST NOT be present." }), marking_ref: stixIdentifierSchema.optional().meta({ description: "The marking_ref property specifies the ID of the marking-definition object that describes the marking. If the lang property is not present, this property MUST be present. If the lang property is present, this property MUST NOT be present." }), selectors: stixListOfString.meta({ description: "The selectors property specifies a list of selectors for content contained within the STIX Object in which this p