UNPKG

@mitre-attack/attack-data-model

Version:

A TypeScript API for the MITRE ATT&CK data model

1,221 lines (1,197 loc) 51.7 kB
import { markingDefinitionSchema } from "./chunk-CSADRGVR.js"; import { relationshipSchema } from "./chunk-HEP7FZZF.js"; import { matrixSchema } from "./chunk-KATTROA4.js"; import { mitigationSchema } from "./chunk-2IOMDLCJ.js"; import { tacticSchema } from "./chunk-2RATGBIX.js"; import { collectionSchema } from "./chunk-CS5ZWOZQ.js"; import { dataSourceSchema } from "./chunk-2JWTU7IT.js"; import { identitySchema } from "./chunk-GXIR2SFM.js"; import { assetSchema } from "./chunk-VYLYNBMT.js"; import { attackBaseDomainObjectSchema } from "./chunk-ZHQVMLOZ.js"; import { killChainPhaseSchema } from "./chunk-JHF4PVSU.js"; import { AttackMotivationOV, AttackResourceLevelOV, ImplementationLanguageOV, MalwareCapabilityOV, MalwareTypeOV, ProcessorArchitectureOV, ToolTypeOV } from "./chunk-VOGK6XDT.js"; import { stixTimestampSchema } from "./chunk-QVEHTIAE.js"; import { objectMarkingRefsSchema, stixCreatedByRefSchema } from "./chunk-BUEHDLBB.js"; import { aliasesSchema, descriptionSchema } from "./chunk-DNIVZ2SM.js"; import { createAttackExternalReferencesSchema } from "./chunk-UP3ZMB5U.js"; import { xMitreContributorsSchema, xMitreModifiedByRefSchema } from "./chunk-U55YRJAX.js"; import { createStixIdValidator } from "./chunk-E3OY6DRE.js"; import { createMultiStixTypeValidator, createStixTypeValidator } from "./chunk-PFSYT437.js"; import { attackDomainSchema, xMitreDomainsSchema } from "./chunk-ZEHAFUHO.js"; import { attackIdExamples, attackIdPatterns, createOldMitreAttackIdSchema } from "./chunk-SAFNV2G2.js"; import { emptyStixListErrorMessage, nonEmptyRequiredString, stixListOfString } from "./chunk-KFUJRXYX.js"; import { xMitrePlatformsSchema } from "./chunk-3VOOG6SX.js"; // src/schemas/sdo/analytic.schema.ts import { z as z11 } from "zod/v4"; // src/refinements/index.ts import "zod/v4"; // src/schemas/sdo/campaign.schema.ts import { z } from "zod/v4"; var baseCitationSchema = z.custom( (value) => { if (typeof value !== "string") return false; if (!value.startsWith("(") || !value.endsWith(")")) return false; const content = value.slice(1, -1); const parts = content.split(":"); if (parts.length !== 2) return false; if (parts[0].trim() !== "Citation") return false; if (parts[1].trim() === "") return false; return true; }, { message: "Each citation must conform to the pattern '(Citation: [citation name])'" } ); var multipleCitationsSchema = z.custom( (value) => { if (typeof value !== "string") return false; const citations = value.match(/\(Citation:[^)]+\)/g); if (!citations) return false; return citations.join("") === value && citations.every((citation) => baseCitationSchema.safeParse(citation).success); }, { message: "Must be one or more citations in the form '(Citation: [citation name])' without any separators" } ); var xMitreFirstSeenCitationSchema = multipleCitationsSchema.meta({ description: "One or more citations for when the object was first seen, in the form '(Citation: [citation name])(Citation: [citation name])...', where each `[citation name]` can be found as one of the `source_name` values in the `external_references`." }); var xMitreLastSeenCitationSchema = multipleCitationsSchema.meta({ description: "One or more citations for when the object was last seen, in the form '(Citation: [citation name])(Citation: [citation name])...', where each `[citation name]` can be found as one of the `source_name` values in the `external_references`." }); var campaignBaseSchema = attackBaseDomainObjectSchema.extend({ id: createStixIdValidator("campaign"), type: createStixTypeValidator("campaign"), description: descriptionSchema, external_references: createAttackExternalReferencesSchema("campaign"), x_mitre_domains: xMitreDomainsSchema, x_mitre_modified_by_ref: xMitreModifiedByRefSchema, x_mitre_contributors: xMitreContributorsSchema.optional(), aliases: aliasesSchema, // Optional in STIX but required in ATT&CK first_seen: stixTimestampSchema.meta({ description: "The time that this Campaign was first seen." }), // Optional in STIX but required in ATT&CK last_seen: stixTimestampSchema.meta({ description: "The time that this Campaign was last seen." }), x_mitre_first_seen_citation: xMitreFirstSeenCitationSchema, x_mitre_last_seen_citation: xMitreLastSeenCitationSchema }).required({ created_by_ref: true, // Optional in STIX but required in ATT&CK object_marking_refs: true, // Optional in STIX but required in ATT&CK revoked: true // Optional in STIX but required in ATT&CK }).strict().meta({ description: ` Campaigns represent sets of adversary activities occurring over a specific time period with shared characteristics and objectives. They are defined as [campaign](http://docs.oasis-open.org/cti/stix/v2.0/csprd01/part2-stix-objects/stix-v2.0-csprd01-part2-stix-objects.html#_Toc476230925) objects with additional temporal tracking fields. `.trim() }); var campaignChecks = (ctx) => { createFirstAliasRefinement()(ctx); createCitationsRefinement()(ctx); }; var campaignSchema = campaignBaseSchema.check(campaignChecks); var campaignPartialSchema = campaignBaseSchema.partial().check(campaignChecks); // src/schemas/sdo/data-component.schema.ts import { z as z2 } from "zod/v4"; var xMitreDataSourceRefSchema = createStixIdValidator("x-mitre-data-source").meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** STIX ID of the data source this component is a part of." }); var xMitreLogSourcesSchema = z2.array( z2.object({ name: nonEmptyRequiredString.meta({ description: 'Log source identifier (e.g., "sysmon", "auditd")' }), channel: nonEmptyRequiredString.meta({ description: 'Specific log channel or event type (e.g., "1" for Sysmon Process Creation)' }) }).strict() ).min(1).check((ctx) => { validateNoDuplicates( [], ["name", "channel"], "Duplicate log source found: each (name, channel) pair must be unique" )(ctx); }).meta({ description: ` The \`log_source\` object defines platform-specific collection configurations embedded within data components: **Uniqueness constraints:** - Each \`(name, channel)\` tuple must be unique within a data component's \`x_mitre_log_sources\` array - Log sources are scoped to their containing data component **Example:** A data component for 'Process Creation' might contain log sources for: - Windows: (name: "sysmon", channel: "1") - Linux: (name: "auditd", channel: "SYSCALL") - macOS: (name: "unified_logs", channel: "process") `.trim() }); var dataComponentSchema = attackBaseDomainObjectSchema.extend({ id: createStixIdValidator("x-mitre-data-component"), type: createStixTypeValidator("x-mitre-data-component"), description: descriptionSchema, // Optional in STIX but required in ATT&CK created_by_ref: stixCreatedByRefSchema, // Optional in STIX but required in ATT&CK object_marking_refs: objectMarkingRefsSchema, x_mitre_domains: xMitreDomainsSchema, x_mitre_modified_by_ref: xMitreModifiedByRefSchema, x_mitre_data_source_ref: xMitreDataSourceRefSchema.optional(), // TODO remove in attack spec 4.0.0 / adm release 5.x // TODO change to required in spec release 4.x x_mitre_log_sources: xMitreLogSourcesSchema.optional() }).strict().meta({ description: ` Data components represent specific types of information within a data source that can be used for detection. They are defined as \`x-mitre-data-component\` objects extending the generic [STIX Domain Object pattern](https://docs.oasis-open.org/cti/stix/v2.0/csprd01/part2-stix-objects/stix-v2.0-csprd01-part2-stix-objects.html#_Toc476230920). `.trim() }); // src/schemas/sdo/detection-strategy.schema.ts import { z as z3 } from "zod/v4"; var detectionStrategySchema = attackBaseDomainObjectSchema.extend({ id: createStixIdValidator("x-mitre-detection-strategy"), type: createStixTypeValidator("x-mitre-detection-strategy"), external_references: createAttackExternalReferencesSchema("x-mitre-detection-strategy"), x_mitre_modified_by_ref: xMitreModifiedByRefSchema, x_mitre_contributors: xMitreContributorsSchema, x_mitre_analytic_refs: z3.array(createStixIdValidator("x-mitre-analytic")).nonempty({ error: "At least one analytic ref is required" }).check((ctx) => { validateNoDuplicates( [], [], 'Duplicate reference "{value}" found. Each embedded relationship referenced in x_mitre_analytic_refs must be unique.' )(ctx); }).meta({ description: "Array of STIX IDs referencing `x-mitre-analytic` objects that implement this detection strategy." }), x_mitre_domains: xMitreDomainsSchema }).required({ created_by_ref: true, object_marking_refs: true }).meta({ description: ` Detection strategies define high-level approaches for detecting specific adversary techniques. They serve as containers that organize multiple platform-specific analytics into cohesive detection methodologies. Detection strategies are defined as \`x-mitre-detection-strategy\` objects extending the generic [STIX Domain Object pattern](https://docs.oasis-open.org/cti/stix/v2.0/csprd01/part2-stix-objects/stix-v2.0-csprd01-part2-stix-objects.html#_Toc476230920). `.trim() }); // src/schemas/sdo/group.schema.ts import { z as z4 } from "zod/v4"; var groupBaseSchema = attackBaseDomainObjectSchema.extend({ id: createStixIdValidator("intrusion-set"), type: createStixTypeValidator("intrusion-set"), // Not used in ATT&CK Group but defined in STIX description: nonEmptyRequiredString.optional().meta({ description: "A description that provides more details and context about the Intrusion Set, potentially including its purpose and its key characteristics" }), // Optional in STIX but required in ATT&CK external_references: createAttackExternalReferencesSchema("intrusion-set"), x_mitre_domains: xMitreDomainsSchema, x_mitre_contributors: xMitreContributorsSchema.optional(), x_mitre_modified_by_ref: xMitreModifiedByRefSchema.optional(), aliases: aliasesSchema.optional().meta({ description: "Alternative names used to identify this group. The first alias must match the object's name" }), // Not used in ATT&CK Group but defined in STIX first_seen: stixTimestampSchema.optional().meta({ description: "The time that this Intrusion Set was first seen" }), // Not used in ATT&CK Group but defined in STIX last_seen: stixTimestampSchema.optional().meta({ description: "The time that this Intrusion Set was last seen" }), // Not used in ATT&CK Group but defined in STIX goals: stixListOfString.optional().meta({ description: "The high-level goals of this Intrusion Set, namely, what are they trying to do" }), // Not used in ATT&CK Group but defined in STIX resource_level: AttackResourceLevelOV.optional().meta({ description: "This property specifies the organizational level at which this Intrusion Set typically works, which in turn determines the resources available to this Intrusion Set for use in an attack" }), primary_motivation: AttackMotivationOV.optional().meta({ description: "The primary reason, motivation, or purpose behind this Intrusion Set" }), secondary_motivations: z4.array(AttackMotivationOV).min(1).optional().meta({ description: "The secondary reasons, motivations, or purposes behind this Intrusion Set" }) }).strict().meta({ description: ` Groups represent clusters of adversary activity with shared characteristics, tools, tactics, or infrastructure. They are defined as [intrusion-set](https://docs.oasis-open.org/cti/stix/v2.0/csprd01/part2-stix-objects/stix-v2.0-csprd01-part2-stix-objects.html#_Toc476230941) objects and strictly follow the STIX 2.1 specification without additional custom fields. `.trim() }); var groupChecks = (ctx) => { createFirstAliasRefinement()(ctx); }; var groupSchema = groupBaseSchema.check(groupChecks); var groupPartialSchema = groupBaseSchema.partial().check(groupChecks); // src/schemas/sdo/malware.schema.ts import { z as z5 } from "zod/v4"; var stixFileType = createStixIdValidator("file").meta({ description: "Used to specify the file stixType of the sample_refs property." }); var stixArtifactType = createStixIdValidator("artifact").meta({ description: "Used to specify the artifact stixType of the sample_refs property." }); var malwareBaseSchema = attackBaseDomainObjectSchema.extend({ id: createStixIdValidator("malware").meta({ description: "The unique identifier for this Malware object.", usage: "inherit", status: "inherit", examples: ["malware--01234567-89ab-cdef-0123-456789abcdef"] }), type: createStixTypeValidator("malware").meta({ description: 'The STIX object type for this object, which is always "malware".', usage: "inherit", status: "inherit", examples: ["malware"] }), created_by_ref: stixCreatedByRefSchema.meta({ description: "The ID of the Source object that describes who created this object.", usage: "inherit", status: "inherit", examples: ["identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5"] }), description: descriptionSchema.meta({ description: "A description that provides more details and context about the Malware.", usage: "required", // Optional in STIX, required in ATT&CK status: "active", examples: ["This is a description of the malware."] }), // Malware: Required // Tool: Optional x_mitre_platforms: xMitrePlatformsSchema.optional().meta({ description: "The platforms that the malware targets.", usage: "extension", status: "active", examples: [["Network Devices", "Data Historian"]] }), x_mitre_contributors: xMitreContributorsSchema.optional().meta({ description: "People and organizations who have contributed to this object.", usage: "extension", status: "active", examples: [["John Doe", "Acme Security"]] }), x_mitre_aliases: aliasesSchema.optional().meta({ description: "Alternative names used to identify this software. The first alias must match the object's name.", usage: "extension", status: "active", examples: [["APT28", "Fancy Bear", "Sofacy"]] }), x_mitre_modified_by_ref: xMitreModifiedByRefSchema.meta({ description: "The STIX ID of the identity that last modified this object.", usage: "extension", status: "active", examples: ["identity--c78cb6e5-0c4b-4611-8297-d1b8b55e40b5"] }), x_mitre_domains: xMitreDomainsSchema.meta({ description: "The ATT&CK domains this object belongs to.", usage: "extension", status: "active", examples: [["enterprise-attack"], ["mobile-attack"], ["ics-attack"]] }), aliases: aliasesSchema.optional().meta({ description: "Alternative names used to identify this software.", usage: "unused", // Not used in ATT&CK Malware but defined in STIX status: "inherit", examples: [["APT28", "Fancy Bear", "Sofacy"]] }), is_family: z5.boolean().meta({ description: "Whether the object represents a malware family (if true) or a malware instance (if false)", usage: "inherit", status: "inherit", examples: [] }), malware_types: z5.array(MalwareTypeOV).min(1, { error: emptyStixListErrorMessage }).optional().meta({ description: "A set of categorizations for the malware being described.", usage: "unused", // Not used in ATT&CK Malware but defined in STIX status: "inherit", examples: [["adware", "backdoor"]] }), kill_chain_phases: z5.array(killChainPhaseSchema).min(1, { error: emptyStixListErrorMessage }).optional().meta({ description: "The list of Kill Chain Phases for which this malware can be used.", usage: "unused", // Not used in ATT&CK Malware but defined in STIX status: "inherit", examples: [[{ kill_chain_name: "mitre-attack", phase_name: "execution" }]] }), first_seen: stixTimestampSchema.optional().meta({ description: "The time that this malware instance or malware family was first seen.", usage: "unused", // Not used in ATT&CK Malware but defined in STIX status: "inherit", examples: ["2021-01-01T00:00:00.000Z"] }), last_seen: stixTimestampSchema.optional().meta({ description: "The time that this malware family or malware instance was last seen.", usage: "unused", // Not used in ATT&CK Malware but defined in STIX status: "inherit", examples: ["2021-01-01T00:00:00.000Z"] }), external_references: createAttackExternalReferencesSchema("malware").meta({ description: "A list of external references which refers to non-STIX information.", usage: "required", // Optional in STIX, required in ATT&CK status: "inherit", examples: [ [ { source_name: "mitre-attack", external_id: "S1234" } ] ] }), x_mitre_old_attack_id: createOldMitreAttackIdSchema("malware").optional().meta({ description: "Old ATT&CK IDs that have been replaced by the current ATT&CK ID.", usage: "extension", status: "deprecated", examples: ["MOB-S0012"] }), architecture_execution_envs: z5.array(ProcessorArchitectureOV).min(1, { error: emptyStixListErrorMessage }).optional().meta({ description: "The processor architectures (e.g., x86, ARM, etc.) that the malware instance or family is executable on.", usage: "unused", // Not used in ATT&CK Malware but defined in STIX status: "inherit", examples: [] }), implementation_languages: z5.array(ImplementationLanguageOV).min(1, { error: emptyStixListErrorMessage }).optional().meta({ description: "The programming language(s) used to implement the malware instance or family.", usage: "unused", // Not used in ATT&CK Malware but defined in STIX status: "inherit", examples: [] }), capabilities: z5.array(MalwareCapabilityOV).min(1).optional().meta({ description: "Any of the capabilities identified for the malware instance or family.", usage: "unused", // Not used in ATT&CK Malware but defined in STIX status: "inherit", examples: [] }), sample_refs: z5.array(z5.union([stixArtifactType, stixFileType])).min(1, { error: emptyStixListErrorMessage }).optional().meta({ description: "The sample_refs property specifies a list of identifiers of the SCO file or artifact objects associated with this malware instance(s) or family.", usage: "unused", status: "inherit", examples: [] }), operating_system_refs: z5.array(createMultiStixTypeValidator(["malware", "tool"])).min(1, { error: emptyStixListErrorMessage }).optional().meta({ description: "The operating systems that the malware family or malware instance is executable on. This applies to virtualized operating systems as well as those running on bare metal.\n\nThe value of this property MUST be the identifier for a SCO software object.", usage: "unused", status: "inherit", examples: [] }) }).strict(); var malwareChecks = (ctx) => { createFirstAliasRefinement()(ctx); createFirstXMitreAliasRefinement()(ctx); }; var malwareSchema = malwareBaseSchema.check(malwareChecks); var malwarePartialSchema = malwareBaseSchema.partial().check(malwareChecks); // src/schemas/sdo/software.schema.ts import { z as z7 } from "zod/v4"; // src/schemas/sdo/tool.schema.ts import { z as z6 } from "zod/v4"; var toolBaseSchema = attackBaseDomainObjectSchema.extend({ id: createStixIdValidator("tool").meta({ description: "The unique identifier for this Tool object." }), type: createStixTypeValidator("tool").meta({ description: 'The STIX object type for this object, which is always "tool".' }), created_by_ref: stixCreatedByRefSchema.meta({ description: "The ID of the Source object that describes who created this object." }), description: descriptionSchema, external_references: createAttackExternalReferencesSchema("tool"), // Malware: Required // Tool: Optional x_mitre_platforms: xMitrePlatformsSchema.optional(), x_mitre_contributors: xMitreContributorsSchema.optional(), x_mitre_aliases: aliasesSchema.optional().meta({ description: "Alternative names used to identify this software. The first alias must match the object's name." }), x_mitre_modified_by_ref: xMitreModifiedByRefSchema, x_mitre_domains: xMitreDomainsSchema, // Not used in ATT&CK Malware or Tool but defined in STIX aliases: aliasesSchema.optional().meta({ description: "Alternative names used to identify this software." }), // Not used in ATT&CK Tool but defined in STIX tool_types: z6.array(ToolTypeOV).min(1, { error: emptyStixListErrorMessage }).optional().meta({ description: "The kind(s) of tool(s) being described." }), // Not used in ATT&CK Tool but defined in STIX kill_chain_phases: z6.array(killChainPhaseSchema).min(1, { error: emptyStixListErrorMessage }).optional().meta({ description: "The list of kill chain phases for which this Tool can be used." }), // Not used in ATT&CK Tool but defined in STIX tool_version: nonEmptyRequiredString.optional().meta({ description: "The version identifier associated with the Tool" }), x_mitre_old_attack_id: createOldMitreAttackIdSchema("tool").optional() }).strict(); var toolChecks = (ctx) => { createFirstXMitreAliasRefinement()(ctx); createFirstAliasRefinement()(ctx); }; var toolSchema = toolBaseSchema.check(toolChecks); var toolPartialSchema = toolBaseSchema.partial().check(toolChecks); // src/schemas/sdo/software.schema.ts var softwareSchema = z7.union([malwareSchema, toolSchema]).meta({ description: "Software represents tools and malicious code used by adversaries to accomplish their objectives. ATT&CK models software using two STIX object types: [malware](http://docs.oasis-open.org/cti/stix/v2.0/csprd01/part2-stix-objects/stix-v2.0-csprd01-part2-stix-objects.html#_Toc476230945) and [tool](http://docs.oasis-open.org/cti/stix/v2.0/csprd01/part2-stix-objects/stix-v2.0-csprd01-part2-stix-objects.html#_Toc476230961)." }); // src/schemas/sdo/technique.schema.ts import { z as z8 } from "zod/v4"; var xMitreNetworkRequirementsSchema = z8.boolean().meta({ description: "Requires network to execute the technique" }); var supportedMitreEffectivePermissions = ["Administrator", "SYSTEM", "User", "root"]; var xMitreEffectivePermissionsSchema = z8.array( z8.enum(supportedMitreEffectivePermissions, { error: () => `Effective permission must be one of: ${supportedMitreEffectivePermissions.join(", ")}` }), { error: (issue) => issue.code === "invalid_type" ? "x_mitre_effective_permissions must be an array of strings" : "Invalid effective permissions array" } ).min(1, { error: "At least one effective permission is required" }).refine((items) => new Set(items).size === items.length, { message: "Effective permissions must be unique (no duplicates allowed)" }).meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** The level of permissions the adversary will attain by performing the technique" }); var supportedMitreImpactTypes = ["Availability", "Integrity"]; var xMitreImpactTypeSchema = z8.array( z8.enum(supportedMitreImpactTypes, { error: () => `Impact type must be one of: ${supportedMitreImpactTypes.join(", ")}` }) ).min(1).meta({ description: "Denotes if the technique can be used for integrity or availability attacks. **Only used in Enterprise domain in the _Impact_ tactic.**" }); var xMitreSystemRequirementsSchema = stixListOfString.meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** Additional information on requirements the adversary needs to meet or about the state of the system (software, patch level, etc.) that may be required for the technique to work" }); var xMitreRemoteSupportSchema = z8.boolean().meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** If true, the technique can be used to execute something on a remote system." }); var supportedMitrePermissionsRequired = [ "Remote Desktop Users", "SYSTEM", "Administrator", "root", "User" ]; var xMitrePermissionsRequiredSchema = z8.array( z8.enum(supportedMitrePermissionsRequired, { error: () => "x_mitre_permissions_required may only contain values from the following list: " + supportedMitrePermissionsRequired.join(", ") }), { error: (issue) => issue.code === "invalid_type" ? "x_mitre_permissions_required must be an array of strings" : "Invalid x_mitre_permissions_required array" } ).min(1, { error: "At least one permission level is required" }).meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** The lowest level of permissions the adversary is required to be operating within to perform the technique on a system." }); var xMitreDataSourceSchema = z8.custom( (value) => { if (typeof value !== "string") return false; const parts = value.split(":"); return parts.length === 2 && parts[0].trim() !== "" && parts[1].trim() !== ""; }, { message: "Each entry must conform to the pattern '<Data Source Name>: <Data Component Name>'" } ).meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** A single data source in the format 'Data Source Name: Data Component Name'" }); var xMitreDataSourcesSchema = z8.array(xMitreDataSourceSchema, { error: (issue) => issue.code === "invalid_type" ? "x_mitre_data_sources must be an array of strings" : "Invalid data sources array" }).min(1, { error: "At least one data source is required" }).meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** Sources of information that may be used to identify the action or result of the action being performed" }); var xMitreIsSubtechniqueSchema = z8.boolean({ error: "x_mitre_is_subtechnique must be a boolean" }).meta({ description: "If true, this attack-pattern is a sub-technique" }); var supportedMitreTacticTypes = [ "Post-Adversary Device Access", "Pre-Adversary Device Access", // TODO only used with PRE-ATT&CK "Without Adversary Device Access" ]; var xMitreTacticTypeSchema = z8.array( z8.enum(supportedMitreTacticTypes, { error: () => `Tactic type must be one of: ${supportedMitreTacticTypes.join(", ")}` }), { error: (issue) => issue.code === "invalid_type" ? "x_mitre_tactic_type must be an array of strings" : "Invalid tactic type array" } ).min(1).meta({ description: '"Post-Adversary Device Access", "Pre-Adversary Device Access", or "Without Adversary Device Access". **Only used in Mobile domain.**' }); var supportedMitreDefenseBypasses = [ "Signature-based detection", "Multi-Factor Authentication", "Network Intrusion Detection System", "Application Control", "Host forensic analysis", "Exploit Prevention", "Signature-based Detection", "Data Execution Prevention", "Heuristic Detection", "File system access controls", "File Monitoring", "Digital Certificate Validation", "Logon Credentials", "Firewall", "Host Forensic Analysis", "Static File Analysis", "Heuristic detection", "Notarization", "System access controls", "Binary Analysis", "Web Content Filters", "Network intrusion detection system", "Host intrusion prevention systems", "Application control", "Defensive network service scanning", "User Mode Signature Validation", "Encryption", "Log Analysis", "Autoruns Analysis", "Anti Virus", "Gatekeeper", "Anti-virus", "Log analysis", "Process whitelisting", "Host Intrusion Prevention Systems", "Windows User Account Control", "System Access Controls", "Application whitelisting", "Whitelisting by file name or path", "File monitoring" ]; var xMitreDefenseBypassesSchema = z8.array( z8.enum(supportedMitreDefenseBypasses, { error: () => `Defense bypass must be one of: ${supportedMitreDefenseBypasses.join(", ")}` }), { error: (issue) => issue.code === "invalid_type" ? "x_mitre_defense_bypasseed must be an array of strings" : "Invalid defense bypassed type array" } ).min(1, { error: "At least one defense bypass is required" }).refine((items) => new Set(items).size === items.length, { message: "Mitre defense bypasses must be unique (no duplicates allowed)." }).meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** List of defensive tools, methodologies, or processes the technique can bypass." }); var xMitreDetectionSchema = nonEmptyRequiredString.meta({ description: "**DEPRECATED in v3.3.0. Will be removed in v4.0.0.** Strategies for identifying if a technique has been used by an adversary." }); var techniqueBaseSchema = attackBaseDomainObjectSchema.extend({ id: createStixIdValidator("attack-pattern"), type: createStixTypeValidator("attack-pattern"), // Optional in STIX but required in ATT&CK external_references: createAttackExternalReferencesSchema("attack-pattern"), kill_chain_phases: z8.array(killChainPhaseSchema).min(1).optional().meta({ description: "Techniques are associated with Tactics through their `kill_chain_phases` property. When the `kill_chain_name` matches the domain (`mitre-attack`, `mitre-mobile-attack`, or `mitre-ics-attack`), the `phase_name` corresponds to the `x_mitre_shortname` of the associated `x-mitre-tactic` object." }), description: descriptionSchema.optional(), x_mitre_platforms: xMitrePlatformsSchema.optional(), x_mitre_detection: xMitreDetectionSchema.optional(), x_mitre_is_subtechnique: xMitreIsSubtechniqueSchema, x_mitre_data_sources: xMitreDataSourcesSchema.optional(), // TODO remove in attack spec 4.0.0 / adm release 5.x x_mitre_defense_bypassed: xMitreDefenseBypassesSchema.optional(), // TODO remove in attack spec 4.0.0 / adm release 5.x x_mitre_contributors: xMitreContributorsSchema.optional(), x_mitre_permissions_required: xMitrePermissionsRequiredSchema.optional(), // TODO remove in attack spec 4.0.0 / adm release 5.x x_mitre_remote_support: xMitreRemoteSupportSchema.optional(), // TODO remove in attack spec 4.0.0 / adm release 5.x x_mitre_system_requirements: xMitreSystemRequirementsSchema.optional(), // TODO remove in attack spec 4.0.0 / adm release 5.x x_mitre_impact_type: xMitreImpactTypeSchema.optional(), x_mitre_effective_permissions: xMitreEffectivePermissionsSchema.optional(), // TODO remove in attack spec 4.0.0 / adm release 5.x x_mitre_network_requirements: xMitreNetworkRequirementsSchema.optional(), x_mitre_tactic_type: xMitreTacticTypeSchema.optional(), x_mitre_domains: xMitreDomainsSchema, x_mitre_modified_by_ref: xMitreModifiedByRefSchema.optional() }).strict().meta({ description: ` Techniques describe specific methods adversaries use to achieve tactical objectives and are represented as [attack-pattern](http://docs.oasis-open.org/cti/stix/v2.0/csprd01/part2-stix-objects/stix-v2.0-csprd01-part2-stix-objects.html#_Toc476230921) objects following the STIX 2.1 specification. ### Sub-Techniques Sub-techniques are specialized implementations of parent techniques, providing more granular detail about adversary methods. They are represented as \`attack-pattern\` objects with the same structure as techniques but include additional constraints and relationships. **Sub-technique characteristics:** - **Identification:** Marked by \`x_mitre_is_subtechnique = true\` - **Parent relationship:** Connected via \`subtechnique-of\` relationship where \`source_ref\` is the sub-technique and \`target_ref\` is the parent technique - **Cardinality:** Each sub-technique has exactly one parent technique; parent techniques may have multiple sub-techniques **Inheritance rules:** - **ATT&CK ID format:** Sub-technique IDs follow the pattern \`Txxxx.yyy\`, where \`Txxxx\` is the parent technique ID and \`yyy\` is the unique sub-technique identifier - **STIX ID uniqueness:** Sub-techniques maintain globally unique STIX IDs despite sharing parent ID prefixes - **Tactic inheritance:** Sub-techniques inherit all tactics from their parent technique - **Platform constraints:** Sub-techniques must use a subset of their parent technique's platforms `.trim() }); var techniqueChecks = (ctx) => { createAttackIdInExternalReferencesRefinement()(ctx); createEnterpriseOnlyPropertiesRefinement()(ctx); createMobileOnlyPropertiesRefinement()(ctx); }; var techniqueSchema = techniqueBaseSchema.check(techniqueChecks); var techniquePartialSchema = techniqueBaseSchema.partial().check(techniqueChecks); // src/schemas/sdo/stix-bundle.schema.ts import { z as z9 } from "zod/v4"; var schemaMap = { get malware() { return malwareSchema; }, get "x-mitre-asset"() { return assetSchema; }, get campaign() { return campaignSchema; }, get "x-mitre-collection"() { return collectionSchema; }, get "x-mitre-data-component"() { return dataComponentSchema; }, get "x-mitre-data-source"() { return dataSourceSchema; }, get "x-mitre-detection-strategy"() { return detectionStrategySchema; }, get "x-mitre-analytic"() { return analyticSchema; }, get identity() { return identitySchema; }, get "x-mitre-matrix"() { return matrixSchema; }, get tool() { return toolSchema; }, get "x-mitre-tactic"() { return tacticSchema; }, get "attack-pattern"() { return techniqueSchema; }, get "intrusion-set"() { return groupSchema; }, get "course-of-action"() { return mitigationSchema; }, get relationship() { return relationshipSchema; }, get "marking-definition"() { return markingDefinitionSchema; } }; var attackObjectsSchema = z9.array( z9.object({ // Basic structure validation to ensure we have a type field type: nonEmptyRequiredString, id: nonEmptyRequiredString }).loose().check((ctx) => { const type = ctx.value.type; const schema = schemaMap[type]; if (!schema) { ctx.issues.push({ code: "custom", message: `Unknown STIX type: ${type}`, path: ["type"], input: ctx.value.type }); return; } const result = z9.safeParse(schema, ctx.value); if (!result.success) { result.error.issues.forEach((issue) => { ctx.issues.push({ code: "custom", message: issue.message, path: issue.path, input: issue.input ?? ctx.value }); }); } }) ).min(1, { message: "The STIX bundle must contain at least one object." }); var stixBundleSchema = z9.object({ id: createStixIdValidator("bundle"), type: createStixTypeValidator("bundle"), objects: z9.array( z9.discriminatedUnion("type", [ collectionSchema, analyticSchema, assetSchema, campaignSchema, dataComponentSchema, dataSourceSchema, detectionStrategySchema, groupSchema, identitySchema, malwareSchema, matrixSchema, mitigationSchema, tacticSchema, techniqueSchema, toolSchema ]) ).min(1) }).meta({ description: "A Bundle is a collection of arbitrary STIX Objects grouped together in a single container. A Bundle does not have any semantic meaning and the objects contained within the Bundle are not considered related by virtue of being in the same Bundle. A STIX Bundle Object is not a STIX Object but makes use of the type and id Common Properties." }).strict().check((ctx) => { validateXMitreCollection()(ctx); validateXMitreContentsReferences()(ctx); validateNoDuplicates( ["objects"], ["id"], 'Duplicate object with id "{id}" found. Each object in the bundle must have a unique id.' )(ctx); }); // src/refinements/index.ts function createFirstAliasRefinement() { return (ctx) => { if (ctx.value.aliases && ctx.value.aliases.length > 0 && ctx.value.name) { if (ctx.value.aliases[0] !== ctx.value.name) { ctx.issues.push({ code: "custom", message: "The first alias must match the object's name.", path: ["aliases"], input: ctx.value.aliases }); } } }; } function createFirstXMitreAliasRefinement() { return (ctx) => { if (ctx.value.x_mitre_aliases && ctx.value.x_mitre_aliases.length > 0 && ctx.value.name) { if (ctx.value.x_mitre_aliases[0] !== ctx.value.name) { ctx.issues.push({ code: "custom", message: "The first alias must match the object's name.", path: ["x_mitre_aliases"], input: ctx.value.x_mitre_aliases }); } } }; } function createCitationsRefinement() { return (ctx) => { const { external_references, x_mitre_first_seen_citation, x_mitre_last_seen_citation } = ctx.value; if (!Array.isArray(external_references)) { return; } const extractCitationNames = (citations) => { const matches = citations.match(/\(Citation: ([^)]+)\)/g); return matches ? matches.map((match) => match.slice(10, -1).trim()) : []; }; const validateCitationString = (citations, path) => { const citationNames = extractCitationNames(citations); citationNames.forEach((citationName, index) => { const citationExists = external_references.some((ref) => ref.source_name === citationName); if (!citationExists) { ctx.issues.push({ code: "custom", message: `Citation ${citationName} not found in external_references.`, path: [...path, index], input: citationName }); } }); if (!citations.match(/^(\(Citation: [^)]+\))+$/)) { ctx.issues.push({ code: "custom", message: "Citations must be in the format '(Citation: Name1)(Citation: Name2)...' without any separators.", path, input: citations }); } }; if (x_mitre_first_seen_citation) { validateCitationString(x_mitre_first_seen_citation, ["x_mitre_first_seen_citation"]); } if (x_mitre_last_seen_citation) { validateCitationString(x_mitre_last_seen_citation, ["x_mitre_last_seen_citation"]); } }; } function validateXMitreCollection() { return (ctx) => { const objects = ctx.value.objects; if (objects.length === 0) { return; } const firstObject = objects[0]; if (firstObject.type !== "x-mitre-collection") { ctx.issues.push({ code: "custom", message: "The first object in the 'objects' array must be of type 'x-mitre-collection'", path: ["objects", 0, "type"], input: firstObject.type }); } const collectionObjects = objects.filter((obj) => obj.type === "x-mitre-collection"); if (collectionObjects.length > 1) { objects.forEach((obj, index) => { if (index > 0 && obj.type === "x-mitre-collection") { ctx.issues.push({ code: "custom", message: "Only one 'x-mitre-collection' object is allowed in the bundle. Found multiple collection objects.", path: ["objects", index, "type"], input: obj.type }); } }); } }; } function validateNoDuplicates(arrayPath, keys, errorMessage) { return (ctx) => { let arr = ctx.value; for (const pathSegment of arrayPath) { if (arr && typeof arr === "object") { arr = arr[pathSegment]; } else { return; } } if (!Array.isArray(arr)) { return; } const seen = /* @__PURE__ */ new Map(); arr.forEach((item, index) => { const keyValues = keys.length === 0 ? [String(item)] : keys.map((key) => { const value = item?.[key]; return value !== void 0 ? String(value) : ""; }); const compositeKey = keyValues.join("||"); if (seen.has(compositeKey)) { const keyValuePairs = keys.reduce( (acc, key, i) => { acc[key] = keyValues[i]; return acc; }, {} ); let message = errorMessage; if (!message) { if (keys.length === 0) { message = `Duplicate value "${keyValues[0]}" found at index ${index}. Previously seen at index ${seen.get(compositeKey)}.`; } else if (keys.length === 1) { message = `Duplicate object with ${keys[0]}="${keyValues[0]}" found at index ${index}. Previously seen at index ${seen.get(compositeKey)}.`; } else { const keyPairs = keys.map((key, i) => `${key}="${keyValues[i]}"`).join(", "); message = `Duplicate object with ${keyPairs} found at index ${index}. Previously seen at index ${seen.get(compositeKey)}.`; } } else { message = message.replace(/\{(\w+)\}/g, (match, key) => { if (key === "index") return String(index); if (key === "value" && keys.length === 0) return keyValues[0]; return keyValuePairs[key] ?? match; }); } ctx.issues.push({ code: "custom", message, path: keys.length === 0 ? [...arrayPath, index] : [...arrayPath, index, ...keys], input: keys.length === 0 ? item : keys.length === 1 ? item?.[keys[0]] : keyValuePairs }); } else { seen.set(compositeKey, index); } }); }; } function validateXMitreContentsReferences() { return (ctx) => { const collectionObject = ctx.value.objects[0]; const collectionContents = collectionObject.x_mitre_contents; if (!collectionContents) { return; } const objectIds = new Set(ctx.value.objects.map((obj) => obj.id)); collectionContents.forEach((contentRef, index) => { const ref = contentRef.object_ref; if (!objectIds.has(ref)) { ctx.issues.push({ code: "custom", message: `STIX ID "${ref}" referenced in x_mitre_contents is not present in the bundle's objects array`, path: ["objects", 0, "x_mitre_contents", index, "object_ref"], input: ref }); } }); }; } function createAttackIdInExternalReferencesRefinement() { return (ctx) => { if (ctx.value.external_references === void 0) { return; } if (ctx.value.x_mitre_is_subtechnique === void 0) { return; } if (!Array.isArray(ctx.value.external_references) || ctx.value.external_references.length === 0) { ctx.issues.push({ code: "custom", message: "At least one external reference with an ATT&CK ID is required.", path: ["external_references"], input: ctx.value.external_references }); return; } const attackIdEntry = ctx.value.external_references[0]; if (!attackIdEntry || typeof attackIdEntry !== "object") { ctx.issues.push({ code: "custom", message: "First external reference must be a valid object.", path: ["external_references", 0], input: attackIdEntry }); return; } if (!attackIdEntry.external_id || typeof attackIdEntry.external_id !== "string") { ctx.issues.push({ code: "custom", message: "ATT&CK ID must be defined in the first external_references entry.", path: ["external_references", 0, "external_id"], input: attackIdEntry.external_id }); return; } const idPattern = ctx.value.x_mitre_is_subtechnique ? attackIdPatterns.subtechnique : attackIdPatterns.technique; const message = ctx.value.x_mitre_is_subtechnique ? `The first external_reference must match the ATT&CK ID format ${attackIdExamples.subtechnique}.` : `The first external_reference must match the ATT&CK ID format ${attackIdExamples.technique}.`; if (!idPattern.test(attackIdEntry.external_id)) { ctx.issues.push({ code: "custom", message, path: ["external_references", 0, "external_id"], input: attackIdEntry.external_id }); } if (!attackIdEntry.source_name || attackIdEntry.source_name !== "mitre-attack") { ctx.issues.push({ code: "custom", message: 'The first external_reference must have source_name set to "mitre-attack".', path: ["external_references", 0, "source_name"], input: attackIdEntry.source_name }); } }; } function createEnterpriseOnlyPropertiesRefinement() { return (ctx) => { if (!Array.isArray(ctx.value.x_mitre_domains)) { return; } const inEnterpriseDomain = ctx.value.x_mitre_domains.includes( attackDomainSchema.enum["enterprise-attack"] ); const tactics = ctx.value.kill_chain_phases?.map((tactic) => tactic.phase_name) || []; function validateEnterpriseOnlyField(fieldName, value, requiredTactic = null) { if (value !== void 0) { if (!inEnterpriseDomain) { ctx.issues.push({ code: "custom", message: `${fieldName} is only supported in the 'enterprise-attack' domain.`, path: [fieldName], input: value }); } else if (requiredTactic && ctx.value.kill_chain_phases !== void 0 && !tactics.includes(requiredTactic)) { ctx.issues.push({ code: "custom", message: `${fieldName} is only supported in the ${requiredTactic} tactic.`, path: [fieldName], input: value }); } } } validateEnterpriseOnlyField( "x_mitre_system_requirements", ctx.value.x_mitre_system_requirements ); validateEnterpriseOnlyField( "x_mitre_permissions_required", ctx.value.x_mitre_permissions_required, "privilege-escalation" ); validateEnterpriseOnlyField( "x_mitre_effective_permissions", ctx.value.x_mitre_effective_permissions, "privilege-escalation" ); validateEnterpriseOnlyField( "x_mitre_defense_bypassed", ctx.value.x_mitre_defense_bypassed, "defense-evasion" ); validateEnterpriseOnlyField( "x_mitre_remote_support", ctx.value.x_mitre_remote_support, "execution" ); validateEnterpriseOnlyField("x_mitre_impact_type", ctx.value.x_mitre_impact_type, "impact"); if (ctx.value.x_mitre_data_sources && inEnterpriseDomain && ctx.value.x_mitre_domains.includes(attackDomainSchema.enum["mobile-attack"])) { ctx.issues.push({ code: "custom", message: "x_mitre_data_sources is not supported in the 'mobile-attack' domain.", path: ["x_mitre_data_sources"], input: ctx.value.x_mitre_data_sources }); } }; } function createMobileOnlyPropertiesRefinement() { return (ctx) => { if (!Array.isArray(ctx.value.x_mitre_domains)) { return; } const inMobileDomain = ctx.value.x_mitre_domains.includes( attackDomainSchema.enum["mobile-attack"] ); if (ctx.value.x_mitre_tactic_type?.length && !inMobileDomain) { ctx.issues.push({ code: "custom", message: "x_mitre_tactic_type is only supported in the 'mobile-attack' domain.", path: ["x_mitre_tactic_type"], input: ctx.value.x_mitre_tactic_type }); } if (ctx.value.x_mitre_data_sources && inMobileDomain) { ctx.issues.push({ code: "custom", message: "x_mitre_data_sources is not supported in the 'mobile-attack' domain.", path: ["x_mitre_data_sources"], input: ctx.value.x_mitre_data_sources }); } }; } // src/schemas/sdo/analytic.schema.ts var xMitreLogSourcePermutationName = nonEmptyRequiredString; var xMitreLogSourceReferenceSchema = z11.object({ x_mitre_data_component_ref: createStixIdValidator("x-mitre-data-component"), name: nonEmptyRequiredString.meta({ description: "Log source name from the associated data component's `x_mitre_log_sources` array" }), channel: nonEmptyRequiredString.meta({ description: "Log source channel from the data component's `x_mitre_log_sources` array" }) }).meta({ description: "The `log_source_reference` object links analytics to specific data components with log source details" }); var xMitreLogSourceReferencesSchema = z11.array(xMitreLogSourceReferenceSchema).min(1).check((ctx) => { validateNoDuplicates( [], ["x_mitre_data_component_ref", "name", "channel"], "Duplicate log source reference found: each (x_mitre_data_component_ref, name, channel) tuple must be unique" )(ctx); }).meta({ description: "A list of log source references, which are delineated by a Data Component STIX ID and the (`name`, `channel`) that is being targeted." }); var xMitreMutableElementSchema = z11.object({ field: nonEmptyRequiredString.meta({ description: "Name of the detection field that can be tuned" }), description: nonEmptyRequiredString.meta({ description: "Rationale for tunability and environment-specific considerations" }) }).meta({ description: "The `mutable_element` object defines tunable parameters within analytics" }); var xMitreMutableElementsSchema = z11.array(xMitreMutableElementSchema).min(1).meta({ description: "Environment-specific tuning knobs like TimeWindow, UserContext, or PortRange, so defenders can adapt without changing core behavior." }); var analyticSchema = attackBaseDomainObjectSchema.extend({ id: createStixIdValidator("x-mitre-analytic"), type: createStixTypeValidator("x-mitre-analytic"), description: descriptionSchema, x_mitre_platforms: xMitrePlatformsSchema.max(1).meta({ description: "Target platform for this Analytic." }), // 0 or 1 external_references: createAttackExternalReferencesSchema("x-mitre-analytic"), x_mitre_log_source_references: xMitreLogSourceReferencesSchema.optional(), x_mitre_mutable_elements: xMitreMutableElementsSchema.optional(), x_mitre_domains: xMitreDomainsSchema, x_mitre_modified_by_ref: xMitreModifiedByRefSchema.optional() }).required({ created_by_ref: true, object_marking_refs: true }).strict().meta({ description: ` Analytics contain platform-specific detection logic and represent the implementation details of a detection strategy. They are defined as \`x-mitre-analytic\` objects extending the ge