Assessor_2.0/src/assessment-context/context-recognition.ts

306 lines
27 KiB
TypeScript

/**
* Recognition vocabulary of the context engine (user decision 2026-10-01, tier 2).
*
* A deviation that no catalogued rule, report section or declared technique assigns is matched locally and
* deterministically against distinctive technical terms of each cluster. A match is a suggestion labelled
* `automatically recognized`; it never changes a cluster state, condition count, Work facet, badge, score,
* Result DB row or report statement, and the finding stays in `Other Deviations (not yet catalogued)`.
*
* Only terms that name one attack surface unambiguously belong here. A term that also appears in texts of
* other clusters carries an `unless` guard for exactly those readings. Precision is measured against every
* catalogued rule text and report section title (tests/assessment-context/context-recognition.test.ts); a term
* whose matches contradict the catalogue is narrowed or removed, never kept for recall.
*/
export const CONTEXT_RECOGNITION_VOCABULARY = { id: 'assessment-context-recognition', version: '1.3.0' } as const;
export interface RecognitionTerm {
id: string;
clusterId: string;
/** Distinctive term; matched case-insensitively unless `caseSensitive`. */
pattern: RegExp;
/** Readings of the same words that belong to another cluster; a match is dropped when one of them applies. */
unless?: RegExp;
}
const term = (clusterId: string, id: string, pattern: RegExp, unless?: RegExp): RecognitionTerm =>
({ id: `${clusterId}:${id}`, clusterId, pattern, ...(unless ? { unless } : {}) });
export const CONTEXT_RECOGNITION_TERMS: RecognitionTerm[] = [
// Kerberoasting and related offline cracking of Kerberos material.
term('kerberoasting-exposure', 'kerberoast', /\bkerberoast/i),
term('kerberoasting-exposure', 'spn', /\bSPNs?\b/, /delegation/i),
term('kerberoasting-exposure', 'as-rep', /\bAS-?REP\b|\bpre-?authentication\b/i),
// Vocabulary 1.3.0: readable gMSA passwords are a permission finding; old gMSA passwords are an age finding.
term('kerberoasting-exposure', 'gmsa', /\bg?MSAs?\b|\bmanaged service accounts?\b/i, /\baccess to gMSA passwords\b|\bgMSA objects with old passwords\b/i),
term('kerberoasting-exposure', 'rc4', /\bRC4\b/i),
term('kerberoasting-exposure', 'des-encryption', /\bDES\b(?! ?\()/),
// Kerberos delegation.
term('kerberos-delegation-abuse', 'delegation', /\b(?:unconstrained|constrained|resource-based constrained|authentication) delegation\b|\bprotocol transition\b/i),
term('kerberos-delegation-abuse', 'rbcd', /\bRBCD\b/),
term('kerberos-delegation-abuse', 'add-computers', /\badd computers?\b|\badd computer accounts?\b|\bMachineAccountQuota\b/i),
// AD CS.
term('adcs-escalation', 'esc', /\bESC\d{1,2}\b/),
term('adcs-escalation', 'template', /\bcertificate templates?\b/i),
term('adcs-escalation', 'enrollment', /\bweb enrollment\b|\bcertificate enrollment\b|\bAD ?CS\b|\benterprise CA\b/i),
term('adcs-escalation', 'san', /\bsubjectAltName\b/i),
// Credential relay and coercion.
term('credential-relay-exposure', 'ntlm', /\bNTLMv?1?\b|\bLAN Manager\b/i, /\baudit|\brestrict NTLM\b/i),
term('credential-relay-exposure', 'name-resolution', /\bLLMNR\b|\bNBT-?NS\b|\bNetBIOS\b|\bmDNS\b|\bmulticast (?:DNS|name resolution)\b|\bWPAD\b/i,
/\bname release\b/i),
term('credential-relay-exposure', 'signing', /\b(?:SMB|LDAP) (?:client |server )?signing\b|\bdigitally sign communications\b|\bchannel binding\b/i),
term('credential-relay-exposure', 'spooler', /\bspooler\b/i),
term('credential-relay-exposure', 'coercion', /\bcoerc\w*|\bPetitPotam\b|\bEFSRPC\b/i),
term('credential-relay-exposure', 'hardened-paths', /\bhardened (?:UNC )?paths\b/i),
term('credential-relay-exposure', 'guest-logons', /\binsecure guest logons?\b/i),
term('credential-relay-exposure', 'dns-updates', /\b(?:nonsecure|insecure|unsecured) (?:dynamic )?(?:DNS|updates?)|\bcreate DNS records\b|\bDNS configuration\b/i),
term('credential-relay-exposure', 'legacy-tls', /\bTLS ?1\.[01]\b|\bTls1\.[01]\b|\bSSL ?v?[23]\b/i),
// AD privileged accounts.
term('ad-privileged-account-exposure', 'protected-users', /\bprotected users\b/i),
term('ad-privileged-account-exposure', 'not-delegatable', /\bsensitive and cannot be delegated\b/i),
term('ad-privileged-account-exposure', 'schema-admins', /\bschema admins\b/i),
term('ad-privileged-account-exposure', 'dns-admins', /\bDNS ?Admins\b/i),
term('ad-privileged-account-exposure', 'shadow-credentials', /\bshadow credentials\b/i),
term('ad-privileged-account-exposure', 'builtin-admin', /\b(?:built-in|native) (?:domain )?administrator(?: account)?\b/i,
/\buser account control\b|\badmin approval mode\b|\bold password/i),
term('ad-privileged-account-exposure', 'dcsync', /\bDC ?Sync\b|\breplication rights\b/i),
term('ad-privileged-account-exposure', 'privileged-objects', /\bprivileged (?:objects?|groups?)\b|\boperators groups?\b|\bdomain admins?\b|\benterprise admins\b/i,
/\bcloud\b|\bEntra\b|\brole\b|\bmembership\b|\bchanges?\b|\bnon-expiring\b|\bnever[- ]expir|\badminCount\b/i),
term('ad-privileged-account-exposure', 'admin-accounts', /\badmin accounts?\b|\badmins with\b/i,
/\bcloud\b|\bEntra\b|\bglobal admin|\bsensitive and cannot\b|\bAutoAdminLogon\b|\bbuilt-in\b|\bnative\b|\bold passwords?\b/i),
// AD persistence indicators.
term('ad-persistence-indicators', 'adminsdholder', /\bAdminSDHolder\b|\badminCount\b/i),
term('ad-persistence-indicators', 'krbtgt', /\bkrbtgt\b|\bKRBTGT\b/, /\bdelegation\b|\bRBCD\b/i),
term('ad-persistence-indicators', 'suspicious', /\bsuspicious\b|\babnormal\b|\bephemeral\b|\breanimate\b|\bSID ?History\b|\bprimary group IDs?\b/i,
/\breport suspicious activity\b|\btrust with SID History\b/i),
// AD reconnaissance.
term('ad-reconnaissance-exposure', 'anonymous', /\banonym\w*/i, /\bTeams\b|\bmeetings?\b|\bsharing links?\b|\bSharePoint\b|\bOneDrive\b/i),
term('ad-reconnaissance-exposure', 'rootdse', /\brootDSE\b|\bNSPI\b/i),
term('ad-reconnaissance-exposure', 'prewin2000', /\bpre-?win(?:dows)? ?2000\b|\bPreWin2000\b/i),
term('ad-reconnaissance-exposure', 'netcease', /\bNetCease\b|\bsession enumeration\b/i),
term('ad-reconnaissance-exposure', 'guest-account', /\bguest account is enabled\b|\bbuilt-in guest account\b|\benabled guest account\b/i),
// AD recovery.
term('ad-recovery-gaps', 'backup', /\bAD backup\b|\blast (?:AD )?backup\b|\bbackup of (?:the )?(?:AD|Active Directory)\b/i),
term('ad-recovery-gaps', 'recycle-bin', /\brecycle bin\b/i),
term('ad-recovery-gaps', 'accidental-deletion', /\baccidental deletion\b/i),
term('ad-recovery-gaps', 'dc-redundancy', /\bredundancy\b|\bnumber of DCs\b/i),
// Password guessing.
term('password-guessing-exposure', 'password-policy', /\bpassword (?:policy|length|complexity)\b|\bminimum password\b|\bpassword must meet\b/i,
/\bentra\b|\bdefault settings\b|\bpassword rule settings\b/i),
term('password-guessing-exposure', 'lockout', /\blockout\b/i, /\bsmart lockout\b|\baudit\b/i),
term('password-guessing-exposure', 'never-expires', /\bpassword never expires\b|\bnever[- ]expiring passwords?\b|\bnon-expiring passwords?\b|\bpassword that never expires\b/i),
term('password-guessing-exposure', 'password-not-required', /\bpassword not required\b|\bPASSWD_NOTREQD\b/i),
term('password-guessing-exposure', 'old-passwords', /\bold passwords?\b|\bpassword last set\b|\bpasswords? older than\b/i, /\bcomputers?\b|\bSSO\b|\bkrbtgt\b|\btrust\b/i),
term('password-guessing-exposure', 'weak-password', /\bweak password\b/i),
term('password-guessing-exposure', 'armoring', /\bKerberos armoring\b|\bFAST\b/),
// Lateral movement.
term('lateral-movement-exposure', 'winrm', /\bWinRM\b|\bremote management\b|\bremote shell\b/i, /\bRunAs\b/i),
term('lateral-movement-exposure', 'smbv1', /\bSMB ?v1\b/i),
term('lateral-movement-exposure', 'firewall', /\bfirewall\b/i, /\blog(?:ging)?\b|\blog (?:dropped|successful)\b/i),
term('lateral-movement-exposure', 'rdp', /\bremote desktop\b|\bRDP\b|\bterminal services\b|\bnetwork level authentication\b/i,
/\blog on through\b|\bdeny log on\b|\bpasswords? to be saved\b|\bprompt for password\b|\bnon-exportable\b/i),
term('lateral-movement-exposure', 'laps', /\bLAPS\b/),
term('lateral-movement-exposure', 'network-logon', /\baccess this computer from the network\b|\brequire secure RPC\b/i, /\bdeny access\b/i),
// Host credentials and local privilege.
term('host-credential-privilege-exposure', 'lsass', /\bLSASS\b|\bcustom SSPs?\b|\bSSPs and APs\b/i),
term('host-credential-privilege-exposure', 'uac', /\buser account control\b|\bUAC\b/i),
term('host-credential-privilege-exposure', 'credential-delegation', /\bcredentials? delegation\b|\bnon-exportable credentials\b|\bcredential guard\b/i),
term('host-credential-privilege-exposure', 'user-rights', /\buser rights?\b|\b(?:back up|restore) files and directories\b|\bload and unload device drivers\b/i),
term('host-credential-privilege-exposure', 'elevated-install', /\balways install with elevated\b/i),
term('host-credential-privilege-exposure', 'stored-credentials', /\bAutoAdminLogon\b|\bautomatic logon\b|\bstorage of passwords and credentials\b|\bRunAs credentials\b|\bpasswords? to be saved\b|\bprompt for password\b/i),
// Detection.
// Advanced audit policy subcategories are named "Audit <Subcategory>"; SMB client/server audit options belong to relay.
term('detection-blind-spots', 'audit', /\bAudit (?!client\b|server\b|insecure\b)[A-Z]\w+|\baudit policy\b|\bPowerShell audit\b|\bnot audited\b/),
term('detection-blind-spots', 'logging', /\blogging\b|\bevent log\b|\bsecurity logs?\b|\blog file\b|\btranscription\b|\bscript block\b|\bcommand line in process creation\b/i,
/\bmanage auditing\b/i),
term('detection-blind-spots', 'monitoring', /\bDefender for Identity\b|\btrigger an alert\b|\bsecurity operations center\b/i),
// Malicious code execution.
term('malicious-code-execution-exposure', 'asr', /\battack surface reduction\b|\bASR\b/),
term('malicious-code-execution-exposure', 'defender', /\breal-time protection\b|\bbehavior monitoring\b|\bscript scanning\b|\bEDR\b|\bSmartScreen\b|\bantimalware\b|\bantivirus\b/i),
term('malicious-code-execution-exposure', 'motw', /\bmark of the web\b/i),
term('malicious-code-execution-exposure', 'autorun', /\bAutoRun\b|\bAutoPlay\b/i),
term('malicious-code-execution-exposure', 'scripts', /\bscript extensions\b|\bscripts? engines?\b|\bdownloaded files\b|\bdangerous websites\b/i),
// Patch and configuration drift.
term('patch-configuration-drift', 'updates', /\bwindows update\b|\bmissing (?:security )?(?:updates|patches)\b|\bunpatched\b/i),
term('patch-configuration-drift', 'obsolete', /\bolder OS\b|\bobsolete\b|\bend of (?:life|support)\b|\bout of support\b|\bunsupported (?:OS|operating system)\b|\bnon-supported versions?\b|\bNTFRS\b|\bold protocol\b/i),
term('patch-configuration-drift', 'functional-level', /\bfunctional levels?\b/i, /\barmoring\b/i),
// Cloud privileged access.
term('cloud-privileged-access-exposure', 'global-admin', /\bglobal admin(?:istrator)?s?\b/i, /\btrigger an alert\b/i),
term('cloud-privileged-access-exposure', 'control-plane', /\bcontrol plane\b/i),
term('cloud-privileged-access-exposure', 'role-assignment', /\brole assignments?\b|\bhighly privileged roles?\b/i, /\btrigger an alert\b|\bphishing-resistant\b/i),
term('cloud-privileged-access-exposure', 'cloud-only', /\bcloud-only\b/i),
term('cloud-privileged-access-exposure', 'break-glass', /\bbreak ?glass\b|\bemergency access\b/i),
term('cloud-privileged-access-exposure', 'root-scope', /\broot scope\b|\bmanagement groups?\b/i),
// Cloud sign-in protection.
term('cloud-sign-in-protection-gaps', 'legacy-auth', /\blegacy authentication\b/i),
term('cloud-sign-in-protection-gaps', 'mfa', /\bMFA\b|\bmulti-?factor\b/i, /\badministrat\w*|\badmins\b|\bhighly privileged\b/i),
term('cloud-sign-in-protection-gaps', 'risk-policy', /\bsign-in risk\b|\buser risk\b|\brisky sign-ins?\b|\bhigh risk\b|\binsider risk\b/i),
term('cloud-sign-in-protection-gaps', 'auth-methods', /\bauthentication methods?\b|\bFIDO2\b|\bMicrosoft Authenticator\b|\bone-time passcode\b/i),
term('cloud-sign-in-protection-gaps', 'cloud-password', /\bsmart lockout\b|\bpassword protection\b/i),
term('cloud-sign-in-protection-gaps', 'session', /\bbrowser sessions?\b|\bsign-in frequency\b/i),
// Cloud app consent.
term('cloud-app-consent-exposure', 'consent', /\bconsent\b/i),
term('cloud-app-consent-exposure', 'user-apps', /\badd-ins?\b|\bMy(?:Custom|Marketplace|ReadWriteMailbox)Apps\b|\buser[- ]owned apps\b|\bthird-party (?:apps|storage)\b/i),
term('cloud-app-consent-exposure', 'app-credentials', /\bapplication credentials\b|\bunused applications\b|\bservice principal credentials\b/i),
// Cloud external access.
term('cloud-external-access-exposure', 'guests', /\bguest (?:users?|invites?|inviter|access)\b|\binvite guests?\b/i, /\brequire MFA\b/i),
term('cloud-external-access-exposure', 'external', /\bexternal (?:users?|domains?|sharing)\b/i, /\brole assignments?\b|\bcontrol plane\b|\bforward\w*/i),
term('cloud-external-access-exposure', 'teams-meetings', /\bTeams meetings?\b/i),
term('cloud-external-access-exposure', 'self-signup', /\bemail validation\b|\bemail based subscription\b|\bpublic groups\b/i),
// Registry policy areas of the Microsoft baselines (vocabulary 1.1.0): path segments and value names that name one
// attack surface. Whole product trees (for example Office or Edge) are not mapped, because their settings span
// several clusters; the catalogue itself assigns an Office setting to host credentials.
term('malicious-code-execution-exposure', 'ie-zones', /Internet Settings\\Zones\\|\bInternet Explorer\b|\bActiveX\b|\bURL ?Actions?\b/i),
term('malicious-code-execution-exposure', 'defender-path', /\\(?:Windows|Microsoft) Defender\b/i),
term('malicious-code-execution-exposure', 'macros', /\bVBAWarnings\b|\bblockcontentexecutionfrominternet\b|\bmacros?\b/i),
term('malicious-code-execution-exposure', 'attachments-path', /\\Policies\\Attachments\\|\bSaveZoneInformation\b/i),
term('malicious-code-execution-exposure', 'app-control', /\\Windows\\Safer\b|\\SrpV2\b|\bAppLocker\b|\bWDAC\b/i),
term('lateral-movement-exposure', 'firewall-path', /\\WindowsFirewall\b/i, /\\Logging\b/i),
term('detection-blind-spots', 'firewall-logging-path', /\\WindowsFirewall\\[^\n]*\\Logging\b/i),
term('detection-blind-spots', 'powershell-path', /\\Windows\\PowerShell\\|\bScriptBlockLogging\b|\bEnableTranscripting\b/i, /\bExecutionPolicy\b/i),
term('credential-relay-exposure', 'smb-dns-path', /\\Lanman(?:Workstation|Server)\\|\\NetworkProvider\\HardenedPaths|\\Windows NT\\DNSClient\b|\\Windows NT\\Printers\b|\bNetBT\b|\bDisableHTTPPrinting\b/i),
term('host-credential-privilege-exposure', 'credential-path', /\\CredentialsDelegation\b|\\DeviceGuard\b|\\Windows\\Installer\b|\bLsaCfgFlags\b|\bRunAsPPL\b|\bHypervisorEnforcedCodeIntegrity\b|\bHVCIMATRequired\b|\bConfigureSystemGuardLaunch\b/i),
term('data-sharing-exposure', 'telemetry-path', /\\Windows\\DataCollection\\|\\CloudContent\\|\bAllowTelemetry\b/i),
term('patch-configuration-drift', 'update-path', /\\WindowsUpdate\\/i),
// Account policy and user right names of security templates (vocabulary 1.1.0).
term('password-guessing-exposure', 'account-policy-keys', /\b(?:LockoutBadCount|LockoutDuration|ResetLockoutCount|MinimumPasswordLength|PasswordComplexity|PasswordHistorySize|MaximumPasswordAge|MinimumPasswordAge|ClearTextPassword)\b|\bempty password\b|\bpassword not required\b/i),
term('host-credential-privilege-exposure', 'privilege-rights', /\bSe(?:Tcb|CreateToken|Debug|Backup|Restore|LoadDriver|TakeOwnership|Security|Audit|RemoteShutdown|Impersonate|AssignPrimaryToken|IncreaseBasePriority|ManageVolume|SystemEnvironment|Relabel)Privilege\b/),
term('lateral-movement-exposure', 'logon-rights', /\bSe(?:Deny)?(?:Network|RemoteInteractive|Interactive|Batch|Service)LogonRight\b|\bDistributed COM Users\b/),
term('kerberos-delegation-abuse', 'delegation-right', /\bSeEnableDelegationPrivilege\b/),
term('host-credential-privilege-exposure', 'uac-values', /\b(?:EnableLUA|ConsentPromptBehavior(?:Admin|User)|FilterAdministratorToken|EnableInstallerDetection|EnableVirtualization)\b/),
term('host-credential-privilege-exposure', 'browser-passwords', /\bFormSuggest Passwords\b|\bPasswordManagerEnabled\b/i),
term('lateral-movement-exposure', 'laps-policy', /AdmPwd|\bLAPS\b/i),
term('detection-blind-spots', 'ntlm-audit-values', /\bAuditNTLM\w*|\bAuditReceivingNTLMTraffic\b|\bRestrictSendingNTLMTraffic\b/i),
term('malicious-code-execution-exposure', 'office-security', /\\Office\\\d+\.\d\\[^\\\n]+\\security\b|\\security\\trusted locations\b|\bprotected ?view\b|\bxl4macro\w*|\bvbadigsig\w*|\brequireaddinsig\b|\bblockxllfrominternet\b|\benableoneoffformscripts\b/i),
term('malicious-code-execution-exposure', 'internet-settings', /\\Internet Settings\b|\bZoneMap\b|\bUNCAsIntranet\b/i),
term('physical-local-access-exposure', 'screen-lock', /\bScreenSave\w*|\bInactivityTimeoutSecs\b|\bmachine inactivity\b/i),
// AD findings without an own cluster word so far (vocabulary 1.1.0).
term('ad-privileged-account-exposure', 'ad-permission-delegation', /\bGPO linking delegation\b|\bdelegation where anybody\b|\bunknown account in delegation\b|\btake control of a key domain object\b/i),
term('ad-persistence-indicators', 'change-indicators', /\bchanges? to (?:privileged group membership|Default Domain (?:Controllers )?Policy)\b|\bSYSVOL Executable Changes\b|\bobjects created within\b/i),
term('cloud-sign-in-protection-gaps', 'seamless-sso', /\bAzure ?AD SSO\b|\bSeamless SSO\b|\bSSO computer account\b|\bAZUREADSSOACC\b/i),
// Entra and Microsoft 365 findings (vocabulary 1.1.0).
term('cloud-sign-in-protection-gaps', 'security-defaults', /\bsecurity defaults\b|\bmodern authentication\b/i),
term('cloud-app-consent-exposure', 'app-registration', /\bregister (?:custom )?applications\b|\ballowed to create apps\b|\bapp registrations?\b/i),
term('cloud-external-access-exposure', 'skype-federation', /\bSkype users\b|\bstorage providers\b/i),
term('email-spoofing-phishing-exposure', 'mailtips', /\bMailTips\b/i),
term('host-credential-privilege-exposure', 'print-driver', /Print_?Driver|\bprint driver installation\b/i),
term('detection-blind-spots', 'alerts-enabled', /\balerts? SHALL be enabled\b/i),
term('data-sharing-exposure', 'customer-lockbox', /\bcustomer lockbox\b/i),
term('cloud-privileged-access-exposure', 'rmau-sspr', /\bRMAU\b|\brestricted management administrative units?\b|\bself-service password reset enabled for privileged\b/i),
// Physical and local access (vocabulary 1.1.0): disk encryption, sleep states, DMA and lock screen.
term('physical-local-access-exposure', 'bitlocker', /\bBitLocker\b|\\FVE\\|\bFVE\b/i),
term('physical-local-access-exposure', 'sleep', /\bPowerSettings\b|\bstandby\b|\bsleep\w*|\bhibernat\w*|\bwakes?\b/i),
term('physical-local-access-exposure', 'dma', /\bDMA\b|\bThunderbolt\b/i),
term('physical-local-access-exposure', 'lock-screen', /\block screen\b|\bcamera\b|\bbiometric\w*/i),
term('detection-blind-spots', 'eventlog-path', /\bEventLog\b/),
// Findings measured as unplaced on 2026-10-12 (vocabulary 1.2.0): antivirus scan options, inactive domain controllers
// and trusts (user decision: trusts belong to recovery), delegated permissions on AD OUs, Defender for Identity
// honeytokens and VPN integration.
term('malicious-code-execution-exposure', 'defender-scan', /\b(?:full|quick) scan\b|\bmalware\b|\bscan (?:removable|mapped) (?:network )?drives?\b/i,
/\bmalware filter\w*|\banti-?malware polic\w*|\bcommon attachment\w*/i),
term('ad-recovery-gaps', 'inactive-dc', /\bDCs? inactive\b|\binactive (?:DCs?|domain controllers?)\b/i),
term('ad-recovery-gaps', 'trust', /\btrusts?\b/i, /\bfor delegation\b|\bTrust Center\b|\bzero trust\b|\bServer Trust Account\b/i),
term('ad-privileged-account-exposure', 'ou-delegated-permission', /\bdelegated permissions? on (?:AD )?(?:OUs?|organizational units?)\b|\bpermissions? (?:on|over) (?:AD )?(?:OUs?|organizational units?)\b|\bOU delegation\b/i,
/\bGraph\b|\bapplications?\b|\bapps?\b|\bconsent\b|\bEntra\b|\bAPI\b/i),
term('detection-blind-spots', 'mdi-deception', /\bhoneytokens?\b|\bhoney ?pots?\b|\bVPN integration\b/i),
// Catalogue 1.12.0 (vocabulary 1.3.0): distinctive words of the newly catalogued Purple Knight and Maester rules.
term('password-guessing-exposure', 'recoverable-passwords', /\breversible (?:encryption|passwords?)\b|\buserPassword\b|\bLM Hash\b/i),
term('ad-persistence-indicators', 'directory-tampering', /\bDCShadow\b|\bdisplay specifiers\b|\bdefault security descriptor\b|\bprivileged account creation\b|\breadable PGID\b/i),
term('ad-privileged-account-exposure', 'sensitive-object-rights', /\bRODC\b|\bDPAPI\b|\bschema permissions\b|\bServer Trust Account\b|\bgMSA passwords\b|\bowner is not an administrator\b|\bmore than \d+ privileged accounts\b|\bprivileged users that are disabled\b/i),
term('adcs-escalation', 'certificate-mapping', /\baltSecurityIdentities\b/i),
term('ad-recovery-gaps', 'dc-state', /\bDomain Controllers? in inconsistent state\b|\bnot authenticated to the domain\b/i),
term('malicious-code-execution-exposure', 'gpo-execution', /\blogon script path\b|\bwritable shortcuts?\b/i),
term('cloud-sign-in-protection-gaps', 'password-expiry', /\bexpire passwords\b|\bpasswords SHALL NOT expire\b|\bpassword expiration policy\b/i),
term('cloud-app-consent-exposure', 'app-management', /\bapp management restrictions\b/i),
// Entra identity governance and workload identities map to the existing cloud clusters (user decision 2026-10-12):
// access packages, entitlement catalogs, pending access approvals and dynamic group rule attributes grant access;
// managed identities replace stored application secrets.
term('cloud-privileged-access-exposure', 'identity-governance', /\baccess packages?\b|\bentitlement management\b|\bpending approvals?\b|\bdynamic (?:group|membership) rules?\b/i),
term('cloud-app-consent-exposure', 'managed-identity', /\bmanaged identit(?:y|ies)\b/i, /\bmanaged service accounts?\b/i),
// Defender for Identity posture recommendations name their scope in the title.
term('password-guessing-exposure', 'mdi-account-attributes', /\b(?:unsecure|insecure) account attributes\b/i),
term('credential-relay-exposure', 'mdi-domain-configuration', /\b(?:unsecure|insecure) domain configurations?\b/i),
// Data sharing.
term('data-sharing-exposure', 'telemetry', /\btelemetry\b|\bdiagnostic data\b|\bdata collection\b/i),
term('data-sharing-exposure', 'dlp', /\bDLP\b|\bdata loss prevention\b|\bsensitivity labels?\b|\bretention polic(?:y|ies)\b/i),
term('data-sharing-exposure', 'calendar-sharing', /\bcalendar (?:details )?sharing\b|\bcontact (?:folder )?sharing\b/i),
// Detection: audit logs of cloud services (version 1.1.0).
term('detection-blind-spots', 'audit-log', /\b(?:unified )?audit (?:log|logs|logging)\b|\bmailbox audit\w*|\baudit log search\b/i, /\bmanage auditing\b/i),
// Email spoofing and phishing (catalogue 1.9.0, vocabulary 1.1.0).
term('email-spoofing-phishing-exposure', 'sender-authentication', /\bDKIM\b|\bSPF\b|\bDMARC\b|\bspoof\w*/i),
term('email-spoofing-phishing-exposure', 'phishing', /\bphish\w*/i, /\bphishing-resistant\b|\bscript\w*|\bdouble[- ]click\w*/i),
term('email-spoofing-phishing-exposure', 'threat-protection', /\bsafe (?:links|attachments)\b|\bmalware filter\w*|\banti-?spam\b|\bspam\b|\bzero-hour auto purge\b|\bcommon attachment\w*/i),
term('email-spoofing-phishing-exposure', 'forwarding', /\bauto-?forward\w*|\bautomatic forwarding\b|\bexternal (?:mail )?forwarding\b|\bmail flow\b|\btransport rules?\b|\bSMTP AUTH\b|\bexternal sender\b/i),
// Cloud external access: Teams federation and anonymous meeting access (vocabulary 1.1.0).
term('cloud-external-access-exposure', 'teams-external', /\bTeams\b[^.]*\b(?:external|anonymous|federat\w*|unmanaged|Skype)\b|\banonymous users?\b[^.]*\b(?:meetings?|join|start)\b/i)
];
/**
* Rule families a tool declares in its own rule IDs (vocabulary 1.1.0). A family names a product area, not a single
* setting, so it only estimates a cluster when no term, anchor or similar finding placed the deviation.
*/
export interface RuleFamily { tool: string; pattern: RegExp; clusterId: string; family: string }
export const CONTEXT_RULE_FAMILIES: RuleFamily[] = [
{ tool: 'maester', pattern: /^MS\.EXO\./i, clusterId: 'email-spoofing-phishing-exposure', family: 'MS.EXO (Exchange Online)' },
{ tool: 'maester', pattern: /^MS\.DEFENDER\./i, clusterId: 'email-spoofing-phishing-exposure', family: 'MS.DEFENDER (Defender for Office 365)' },
{ tool: 'maester', pattern: /^ORCA\./i, clusterId: 'email-spoofing-phishing-exposure', family: 'ORCA (Office 365 email protection)' },
{ tool: 'maester', pattern: /^CIS\.M365\.2\./i, clusterId: 'email-spoofing-phishing-exposure', family: 'CIS M365 section 2 (Defender email and collaboration)' },
{ tool: 'maester', pattern: /^MS\.TEAMS\./i, clusterId: 'cloud-external-access-exposure', family: 'MS.TEAMS (Teams)' },
{ tool: 'maester', pattern: /^CIS\.M365\.8\./i, clusterId: 'cloud-external-access-exposure', family: 'CIS M365 section 8 (Teams)' },
{ tool: 'maester', pattern: /^MS\.SHAREPOINT\./i, clusterId: 'cloud-external-access-exposure', family: 'MS.SHAREPOINT (SharePoint and OneDrive)' },
{ tool: 'maester', pattern: /^CIS\.M365\.7\./i, clusterId: 'cloud-external-access-exposure', family: 'CIS M365 section 7 (SharePoint)' },
{ tool: 'maester', pattern: /^MS\.POWERPLATFORM\./i, clusterId: 'data-sharing-exposure', family: 'MS.POWERPLATFORM (Power Platform)' },
{ tool: 'maester', pattern: /^CIS\.M365\.3\./i, clusterId: 'data-sharing-exposure', family: 'CIS M365 section 3 (Purview)' }
];
/** The rule family of a tool's rule ID, as declared in CONTEXT_RULE_FAMILIES. */
export function ruleFamilyOf(tool: string | undefined, ruleId: string | undefined, families: RuleFamily[] = CONTEXT_RULE_FAMILIES): RuleFamily | undefined {
if (!tool || !ruleId) return undefined;
return families.find(item => item.tool === tool && item.pattern.test(ruleId.trim()));
}
export interface RecognitionMatch {
termId: string;
clusterId: string;
/** The exact words of the text that matched, as they appear in it. */
matchedText: string;
}
/** Every distinct cluster a text names, with the first matching term per cluster. Deterministic, local, no inference. */
export function recognizeClusters(text: string, terms: RecognitionTerm[] = CONTEXT_RECOGNITION_TERMS): RecognitionMatch[] {
const matches = new Map<string, RecognitionMatch>();
for (const item of terms) {
if (matches.has(item.clusterId)) continue;
const match = item.pattern.exec(text);
if (!match || item.unless?.test(text)) continue;
matches.set(item.clusterId, { termId: item.id, clusterId: item.clusterId, matchedText: match[0] });
}
return [...matches.values()];
}