nexus-agents 8.28.7 → 8.28.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CDC3SLKC.js → chunk-6QRX5DXU.js} +2 -2
- package/dist/{chunk-544BNTIS.js → chunk-IKGRS6I3.js} +3 -3
- package/dist/{chunk-OMYQAFZ4.js → chunk-IXAPDTFG.js} +2 -2
- package/dist/{chunk-PF3J3P3X.js → chunk-RQXTL7D7.js} +3 -3
- package/dist/{chunk-OALN3JJZ.js → chunk-T4NMCY3E.js} +9 -9
- package/dist/{chunk-SQFVKYLC.js → chunk-THQTN3P7.js} +2 -2
- package/dist/{chunk-PH4EZT26.js → chunk-UDWIG4UI.js} +14 -11
- package/dist/{chunk-PH4EZT26.js.map → chunk-UDWIG4UI.js.map} +1 -1
- package/dist/cli.js +8 -8
- package/dist/{consensus-vote-UK5OJH6S.js → consensus-vote-ZIRXZ27E.js} +4 -4
- package/dist/{improvement-review-OEOX47AK.js → improvement-review-FKINCLP2.js} +4 -4
- package/dist/index.js +7 -7
- package/dist/{issue-triage-QUP2I67F.js → issue-triage-NZLA75GB.js} +2 -2
- package/dist/model-registry.generated.json +5545 -2127
- package/dist/{setup-data-dir-E6YYYUCF.js → setup-data-dir-43IEDUU2.js} +3 -3
- package/package.json +1 -1
- /package/dist/{chunk-CDC3SLKC.js.map → chunk-6QRX5DXU.js.map} +0 -0
- /package/dist/{chunk-544BNTIS.js.map → chunk-IKGRS6I3.js.map} +0 -0
- /package/dist/{chunk-OMYQAFZ4.js.map → chunk-IXAPDTFG.js.map} +0 -0
- /package/dist/{chunk-PF3J3P3X.js.map → chunk-RQXTL7D7.js.map} +0 -0
- /package/dist/{chunk-OALN3JJZ.js.map → chunk-T4NMCY3E.js.map} +0 -0
- /package/dist/{chunk-SQFVKYLC.js.map → chunk-THQTN3P7.js.map} +0 -0
- /package/dist/{consensus-vote-UK5OJH6S.js.map → consensus-vote-ZIRXZ27E.js.map} +0 -0
- /package/dist/{improvement-review-OEOX47AK.js.map → improvement-review-FKINCLP2.js.map} +0 -0
- /package/dist/{issue-triage-QUP2I67F.js.map → issue-triage-NZLA75GB.js.map} +0 -0
- /package/dist/{setup-data-dir-E6YYYUCF.js.map → setup-data-dir-43IEDUU2.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/security/firewall/firewall-policy-mode.ts","../src/security/audit-bridge.ts","../src/security/trust-classifier.ts","../src/security/firewall/firewall-types.ts","../src/security/firewall/agent-trust-labels.ts","../src/security/firewall/firewall-passthrough.ts","../src/security/firewall/firewall-trust-reason.ts","../src/security/policy-gate.ts","../src/security/action-schema.ts","../src/security/corroboration-validator.ts","../src/security/firewall/firewall-pipeline.ts","../src/security/firewall/github-adapter.ts","../src/dogfooding/untrusted-input-firewall.ts","../src/scm/url-parsers.ts","../src/scm/github-provider-traits.ts","../src/dogfooding/issue-triage-types.ts","../src/dogfooding/issue-triage-helpers.ts","../src/dogfooding/issue-triage.ts"],"sourcesContent":["/**\n * Rollout gate for `HostileInputFirewall` behaviour changes (#5382, epic #5281).\n *\n * `HostileInputFirewall` is a PUBLISHED API: re-exported through\n * `src/exports/security.ts`, carried in `api-surface.txt`, and pinned by an\n * export-contract test. Epic #5281 found it has fallen BEHIND the hand-composed\n * production path rather than being dead scaffolding, and its remaining children\n * change what `process()` decides — #5380 raises one policy check to seven,\n * #5381 makes reputation gating mode-aware.\n *\n * A supermajority panel ratified this gate FIRST (6 approve / 1 reject; the lone\n * rejection argued the gate binds even more firmly than proposed) so those\n * changes have somewhere to land that is not a silent behaviour change shipped\n * in a patch release to consumers this repo cannot see.\n *\n * The invariant that makes it a gate: **`off` is the default, and under `off`\n * behaviour is byte-identical to pre-#5382.** A gate whose default changes\n * behaviour has not gated anything.\n *\n * Deliberately NOT a new mechanism. This is the third flag of exactly this shape\n * — `NEXUS_ACCESS_POLICY_MODE` (ClawGuard, #1977) and `NEXUS_REPUTATION_GATING`\n * (#3122) are the other two — so it delegates to the shared `resolveEnvMode`\n * helper (#3130) and coerces identically. `access-constraint-deriver/types.ts`\n * notes these env-var names are a stability contract; adding a fourth resolver\n * with subtly different coercion is the sprawl that helper exists to prevent.\n *\n * @module security/firewall/firewall-policy-mode\n */\n\nimport { z } from 'zod';\nimport { resolveEnvMode } from '../env-mode.js';\nimport type { ILogger } from '../../core/index.js';\n\n/**\n * Rollout state for firewall policy behaviour.\n *\n * - `off` — pre-#5382 behaviour exactly. The default.\n * - `audit` — compute the stricter outcome and REPORT it, but enforce the old\n * one. This is the mode that makes a rollout measurable: it answers \"what\n * would change?\" without changing it.\n * - `enforce` — apply the stricter outcome.\n */\nexport const FirewallPolicyModeSchema = z.enum(['off', 'audit', 'enforce']);\nexport type FirewallPolicyMode = z.infer<typeof FirewallPolicyModeSchema>;\n\n/**\n * Default is `off`, and that is the compatibility promise, not a placeholder.\n *\n * Note this differs from `DEFAULT_REPUTATION_GATING_MODE`, which is `enforce`\n * (#4667) — that flag governs an INTERNAL path this repo owns end to end, and\n * was flipped only after measurement over the real triage path. This one\n * governs a published surface with unknown external callers, so it starts off\n * and stays off until the same kind of measurement justifies a flip. Any future\n * flip is a MAJOR version change, not a patch.\n */\nexport const DEFAULT_FIREWALL_POLICY_MODE: FirewallPolicyMode = 'off';\n\n/** Env var carrying the mode. Named to match its two sibling flags. */\nexport const FIREWALL_POLICY_ENV_VAR = 'NEXUS_FIREWALL_POLICY';\n\n/**\n * Resolve the firewall policy mode from the environment.\n *\n * Never throws, because a security layer must not fail-closed at startup on an\n * operator typo (#3130). The two non-happy paths resolve DIFFERENTLY, though:\n * unset or empty resolves silently to `off` (absence is the normal state), while\n * an explicit-but-invalid value resolves to `audit` and emits one `warn`.\n *\n * @param env Environment to read (injectable for tests).\n * @param logger Injectable for tests; defaults to the shared module logger.\n */\nexport function resolveFirewallPolicyMode(\n env: NodeJS.ProcessEnv = process.env,\n logger?: ILogger\n): FirewallPolicyMode {\n return resolveEnvMode(\n env[FIREWALL_POLICY_ENV_VAR],\n FirewallPolicyModeSchema,\n DEFAULT_FIREWALL_POLICY_MODE,\n FIREWALL_POLICY_ENV_VAR,\n {\n ...(logger !== undefined ? { logger } : {}),\n // An explicit-but-invalid value lands on `audit`, NOT on the unset\n // default `off`. Unset means \"the operator has not opted in\" and `off` is\n // right for it; a typo means \"the operator opted in and mistyped how\",\n // and answering that with the most permissive mode hands them a firewall\n // that refuses nothing while they believe it is on. `audit` reports\n // without refusing, so it cannot break an external caller any more than\n // `off` can, and it emits the `wouldRefuse` telemetry the rollout is\n // supposed to be collecting — which `off` does not.\n invalidFallback: 'audit',\n }\n );\n}\n","/**\n * Security → durable audit bridge (#3291, epic #3288 item 3).\n *\n * The security `AuditTrail` (audit-trail.ts) is in-memory-only, so every\n * trust/policy/reputation/sanitization decision is lost on process exit. The\n * durable `AuditLogger` (audit/) persists with a tamper-evident hash chain but\n * lacks these rich security event types. This bridge maps each security\n * `AuditEvent` (the discriminated union) into a durable `AuditEventInput` and\n * forwards it to an `IAuditLogger`, so security decisions become durable and\n * hash-verifiable — satisfying CLAUDE.md's \"immutable audit\" mandate.\n *\n * Per the #3291 confirmation vote (fold-in design): security events are folded\n * into the durable schema with `category`/`action` distinguishing them\n * (queryable by `action: 'security.*'`), rather than standing up a parallel\n * SecurityAuditLogger.\n *\n * @module security/audit-bridge\n */\n\nimport { createLogger, getErrorMessage } from '../core/index.js';\nimport { createAuditTrail } from './audit-trail.js';\nimport type {\n AuditEvent as SecurityAuditEvent,\n AuditTrail,\n DurableAuditSink,\n} from './audit-trail.js';\nimport type {\n AuditEventInput,\n AuditActor,\n AuditOutcome,\n AuditSeverity,\n IAuditLogger,\n} from '../audit/audit-types.js';\n\nconst logger = createLogger({ component: 'SecurityAuditBridge' });\n\nfunction systemActor(id: string, name?: string): AuditActor {\n return name !== undefined ? { type: 'system', id, name } : { type: 'system', id };\n}\n\nfunction userActor(username: string): AuditActor {\n return { type: 'user', id: username };\n}\n\ntype TrustEvent = Extract<SecurityAuditEvent, { type: 'trust_classification' }>;\ntype PolicyEvent = Extract<SecurityAuditEvent, { type: 'policy_gate' }>;\ntype CorroborationEvt = Extract<SecurityAuditEvent, { type: 'corroboration' }>;\ntype ReputationEvt = Extract<SecurityAuditEvent, { type: 'reputation' }>;\ntype SanitizationEvt = Extract<SecurityAuditEvent, { type: 'sanitization' }>;\ntype GraphEvt = Extract<SecurityAuditEvent, { type: 'graph_execution' }>;\ntype ClawGuardEvt = Extract<SecurityAuditEvent, { type: 'clawguard_violation' }>;\n\nfunction mapTrust(e: TrustEvent): AuditEventInput {\n const severity: AuditSeverity = e.wasDowngraded ? 'warning' : 'info';\n return {\n category: 'authorization',\n severity,\n outcome: 'success',\n action: 'security.trust_classification',\n actor: userActor(e.username),\n description: e.reason,\n metadata: {\n assignedTier: e.assignedTier,\n userRole: e.userRole,\n // Absent when no allowlist was consulted (#4992) — never a default false.\n ...(e.isAllowlisted !== undefined ? { isAllowlisted: e.isAllowlisted } : {}),\n wasDowngraded: e.wasDowngraded,\n component: e.component,\n },\n };\n}\n\n/**\n * Optional pipeline-policy metadata fields, absent on the security path. Extracted\n * so {@link mapPolicyGate} stays under the complexity cap. `actionType`/`mode`/\n * `ruleIds`/`stageType` are the #3710 round-trip fields; `recordKind`/\n * `violationCount` are #3727 (the summary/violation discriminator + per-evaluation\n * count the durable would-block rate needs).\n */\nfunction pipelinePolicyMetadata(e: PolicyEvent): Record<string, unknown> {\n return {\n ...(e.actionType !== undefined ? { actionType: e.actionType } : {}),\n ...(e.mode !== undefined ? { mode: e.mode } : {}),\n ...(e.ruleIds !== undefined ? { ruleIds: e.ruleIds } : {}),\n ...(e.stageType !== undefined ? { stageType: e.stageType } : {}),\n ...(e.recordKind !== undefined ? { recordKind: e.recordKind } : {}),\n ...(e.violationCount !== undefined ? { violationCount: e.violationCount } : {}),\n };\n}\n\nfunction mapPolicyGate(e: PolicyEvent): AuditEventInput {\n const outcome: AuditOutcome = e.allowed ? 'success' : 'denied';\n // #3710/#3727: the pipeline-policy path carries mode/ruleIds/stageType +\n // recordKind/violationCount and no actionType; these MUST round-trip into the\n // durable metadata (see pipelinePolicyMetadata). The security path leaves them\n // undefined and keeps its actionType-keyed shape.\n return {\n category: 'authorization',\n severity: e.allowed ? 'info' : 'warning',\n outcome,\n action: 'security.policy_gate',\n actor: systemActor(e.component),\n policyName: 'security.policy_gate',\n policyDecision: e.allowed ? 'allow' : 'deny',\n ...(e.violationRules.length > 0 ? { violationType: e.violationRules.join(',') } : {}),\n metadata: {\n requiresApproval: e.requiresApproval,\n inputTrustTier: e.inputTrustTier,\n violationRules: e.violationRules,\n ...pipelinePolicyMetadata(e),\n },\n };\n}\n\nfunction mapCorroboration(e: CorroborationEvt): AuditEventInput {\n return {\n category: 'security',\n severity: e.satisfied ? 'info' : 'warning',\n outcome: e.satisfied ? 'success' : 'denied',\n action: 'security.corroboration',\n actor: systemActor(e.component),\n metadata: {\n actionType: e.actionType,\n sourceCount: e.sourceCount,\n missingRequirements: e.missingRequirements,\n },\n };\n}\n\nfunction mapReputation(e: ReputationEvt): AuditEventInput {\n return {\n category: 'security',\n severity: e.isSuspicious ? 'warning' : 'info',\n outcome: 'success',\n action: 'security.reputation',\n actor: userActor(e.username),\n metadata: {\n reputationScore: e.reputationScore,\n isSuspicious: e.isSuspicious,\n effectiveTier: e.effectiveTier,\n signalCount: e.signalCount,\n component: e.component,\n },\n };\n}\n\nfunction mapSanitization(e: SanitizationEvt): AuditEventInput {\n return {\n category: 'security',\n severity: e.injectionFlagCount > 0 ? 'warning' : 'info',\n outcome: 'success',\n action: 'security.sanitization',\n actor: systemActor(e.component, e.source),\n metadata: {\n source: e.source,\n wasModified: e.wasModified,\n ...(e.truncated !== undefined ? { truncated: e.truncated } : {}),\n strippedCount: e.strippedCount,\n injectionFlagCount: e.injectionFlagCount,\n strippedElements: e.strippedElements,\n },\n };\n}\n\nfunction mapGraphExecution(e: GraphEvt): AuditEventInput {\n return {\n category: 'system',\n severity: 'info',\n outcome: 'success',\n action: 'security.graph_execution',\n actor: systemActor(e.component),\n description: e.detail,\n metadata: {\n graphEvent: e.graphEvent,\n ...(e.nodeId !== undefined ? { nodeId: e.nodeId } : {}),\n stepNumber: e.stepNumber,\n },\n };\n}\n\n/**\n * Map a ClawGuard AUDIT-mode violation (#4097) to a durable event. `outcome` is\n * `success` because audit mode ALLOWED the call (it is log-and-allow, not a\n * denial); `severity` is `warning` to flag the policy violation. Queryable by\n * `action: 'security.clawguard_violation'`.\n */\nfunction mapClawGuard(e: ClawGuardEvt): AuditEventInput {\n return {\n category: 'authorization',\n severity: 'warning',\n outcome: 'success',\n action: 'security.clawguard_violation',\n actor: systemActor(e.component),\n metadata: {\n toolName: e.toolName,\n warning: e.warning,\n policySource: e.policySource,\n mode: e.mode,\n requestId: e.requestId,\n },\n };\n}\n\n/**\n * Map a security `AuditEvent` (discriminated union) to a durable\n * `AuditEventInput`. Pure — no side effects. The durable logger assigns\n * id/timestamp/hash, so those are omitted here.\n */\nexport function securityAuditEventToInput(event: SecurityAuditEvent): AuditEventInput {\n switch (event.type) {\n case 'trust_classification':\n return mapTrust(event);\n case 'policy_gate':\n return mapPolicyGate(event);\n case 'corroboration':\n return mapCorroboration(event);\n case 'reputation':\n return mapReputation(event);\n case 'sanitization':\n return mapSanitization(event);\n case 'graph_execution':\n return mapGraphExecution(event);\n case 'clawguard_violation':\n return mapClawGuard(event);\n }\n}\n\n/**\n * Build a {@link SecurityAuditSink} that maps security events into the durable\n * schema and writes them through `auditLogger`. Errors are swallowed and logged\n * — durable mirroring must never break the security pipeline.\n */\nexport function createDurableAuditSink(auditLogger: IAuditLogger): DurableAuditSink {\n return (event: SecurityAuditEvent) => {\n try {\n auditLogger.log(securityAuditEventToInput(event));\n } catch (error) {\n logger.warn('Failed to mirror security event to durable audit log', {\n type: event.type,\n error: getErrorMessage(error),\n });\n }\n };\n}\n\n/**\n * Wrap an optional `auditLogger` into a durable {@link AuditTrail} (#4097) — the\n * single representation of \"mirror security events to the shared hash chain when\n * a logger is threaded.\" Returns undefined when none is present, so the no-logger\n * path establishes NO trail and stays byte-identical (mirrors the dev-pipeline\n * `buildPolicyAuditTrail` guard). Consumers wrap execution in `withAuditTrail`\n * ONLY when this returns a trail.\n */\nexport function createDurableAuditTrail(auditLogger?: IAuditLogger): AuditTrail | undefined {\n if (auditLogger === undefined) return undefined;\n return createAuditTrail(createDurableAuditSink(auditLogger));\n}\n","/**\n * nexus-agents/security - Trust Classifier\n *\n * Classifies GitHub users and content into trust tiers based on\n * repository relationship, allowlist membership, and content analysis.\n * Works with the input sanitizer to determine how agent decisions\n * should weight each input source.\n *\n * @module security/trust-classifier\n * (Source: Issue #818, #819 — Phase 1: Input Sanitization)\n */\n\nimport type { TrustTier, GitHubUserRole, SanitizedInput, SanitizerConfig } from './trust-types.js';\nimport { ROLE_DEFAULT_TRUST, TRUST_TIER_NUMERIC } from './trust-types.js';\n\n// ============================================================================\n// GitHub Author Association Mapping\n// ============================================================================\n\n/**\n * Maps GitHub API author_association values to our GitHubUserRole enum.\n * See: https://docs.github.com/en/graphql/reference/enums#commentauthorassociation\n */\nexport function mapAuthorAssociation(association: string): GitHubUserRole {\n switch (association.toUpperCase()) {\n case 'OWNER':\n return 'owner';\n case 'MEMBER':\n return 'member';\n case 'COLLABORATOR':\n return 'collaborator';\n case 'CONTRIBUTOR':\n return 'contributor';\n case 'FIRST_TIMER':\n case 'FIRST_TIME_CONTRIBUTOR':\n case 'NONE':\n return 'unknown';\n case 'MANNEQUIN':\n return 'unknown';\n default:\n return 'unknown';\n }\n}\n\n// ============================================================================\n// Trust Classification\n// ============================================================================\n\n/**\n * Input for trust classification.\n */\nexport interface ClassifyInput {\n /** GitHub username. */\n readonly username: string;\n /** GitHub API author_association value. */\n readonly authorAssociation: string;\n /** Sanitized input (if content has already been through the sanitizer). */\n readonly sanitizedInput?: SanitizedInput;\n /** Sanitizer config (for allowlist check). */\n readonly config?: Partial<SanitizerConfig>;\n}\n\n/**\n * Result of trust classification.\n */\nexport interface ClassifyResult {\n /** Assigned trust tier. */\n readonly trustTier: TrustTier;\n /** GitHub user role. */\n readonly userRole: GitHubUserRole;\n /** Whether the user is on the maintainer allowlist. */\n readonly isAllowlisted: boolean;\n /** Whether content triggered a trust downgrade. */\n readonly wasDowngraded: boolean;\n /** Reason for the assigned tier. */\n readonly reason: string;\n}\n\n/**\n * Classifies a GitHub user and their content into a trust tier.\n *\n * The trust tier is determined by:\n * 1. Allowlist membership (always Tier 1)\n * 2. GitHub author_association → role → default tier\n * 3. Content injection analysis (can only downgrade, never upgrade)\n *\n * ⚠ **Use HostileInputFirewall.process() in agent code paths.** The live\n * paths (`dogfooding/issue-triage`, `dogfooding/pr-reviewer`) route through\n * it as of #4992. Calling classifyTrust() directly emits no audit-trail\n * event, and unless the caller supplies `config.allowlistedMaintainers` no\n * allowlist is consulted — `isAllowlisted: false` is then a default, not a\n * measurement, and must not be recorded as one. Direct use is for unit tests\n * and non-decision analysis only. (The Rule of Two is enforced separately by\n * `evaluatePolicy` in policy-gate; the firewall evaluates it too, as a\n * signal, and refuses on it only under `NEXUS_FIREWALL_POLICY=enforce`.)\n *\n * @see packages/nexus-agents/src/security/firewall/firewall-pipeline.ts\n * @see packages/nexus-agents/src/security/policy-gate.ts\n */\nexport function classifyTrust(input: ClassifyInput): ClassifyResult {\n const allowlistedMaintainers = input.config?.allowlistedMaintainers ?? [];\n const isAllowlisted = allowlistedMaintainers.includes(input.username);\n const userRole = mapAuthorAssociation(input.authorAssociation);\n\n if (isAllowlisted) {\n return {\n trustTier: '1',\n userRole,\n isAllowlisted: true,\n wasDowngraded: false,\n reason: `User ${input.username} is on the maintainer allowlist`,\n };\n }\n\n const baseTier = ROLE_DEFAULT_TRUST[userRole];\n\n // Explicit false means the sanitization stage did not measure content.\n // Absence is legacy sanitizer output and remains measured fail-closed.\n if (input.sanitizedInput !== undefined && input.sanitizedInput.contentTierMeasured !== false) {\n const contentTier = input.sanitizedInput.trustTier;\n const downgraded = TRUST_TIER_NUMERIC[contentTier] > TRUST_TIER_NUMERIC[baseTier];\n\n return {\n trustTier: downgraded ? contentTier : baseTier,\n userRole,\n isAllowlisted: false,\n wasDowngraded: downgraded,\n reason: downgraded\n ? `Downgraded from Tier ${baseTier} to ${contentTier}: injection patterns detected`\n : `Role ${userRole} → Tier ${baseTier}`,\n };\n }\n\n return {\n trustTier: baseTier,\n userRole,\n isAllowlisted: false,\n wasDowngraded: false,\n reason: `Role ${userRole} → Tier ${baseTier}`,\n };\n}\n\n/**\n * Checks whether a trust tier can influence agent decisions.\n * Tiers 3-4 are informational only — they cannot drive actions.\n */\nexport function canInfluenceDecisions(tier: TrustTier): boolean {\n return TRUST_TIER_NUMERIC[tier] <= 2;\n}\n\n/**\n * Checks whether a trust tier requires corroboration with Tier 1 sources.\n * Tier 2 requires corroboration; Tier 1 is self-sufficient.\n */\nexport function requiresCorroboration(tier: TrustTier): boolean {\n return tier === '2';\n}\n\n/**\n * Returns the minimum trust tier required for a given action type.\n * Actions that modify state require higher trust.\n */\nexport function getRequiredTrustTier(actionType: string): TrustTier {\n switch (actionType) {\n case 'GeneratePatchPlan':\n return '1'; // Requires maintainer-level trust\n case 'DraftReply':\n case 'ProposeLabels':\n return '2'; // Requires at least collaborator-level trust\n case 'SummarizeIssue':\n case 'ClassifyIssue':\n case 'IdentifyDuplicates':\n return '3'; // Read-only, can use any input\n case 'RequestHumanApproval':\n case 'RefuseAction':\n return '4'; // Always allowed (safety actions)\n default:\n return '1'; // Unknown actions require highest trust\n }\n}\n","/**\n * nexus-agents/security/firewall - Types\n *\n * Configuration, result, and adapter interface types for the\n * HostileInputFirewall pipeline. Uses Zod schemas for runtime\n * validation at construction boundaries.\n *\n * @module security/firewall/firewall-types\n * (Source: Issue #826 — Reusable Hostile Input Firewall)\n */\n\nimport { z } from 'zod';\nimport type { IAuditLogger } from '../../audit/audit-types.js';\nimport type { FirewallPolicyMode } from './firewall-policy-mode.js';\nimport type {\n ReputationAssessment,\n ReputationGatingMode,\n GitHubUserMetadata,\n} from '../reputation-model.js';\n\n// ============================================================================\n// Source Adapter Interface\n// ============================================================================\n\n/**\n * Metadata extracted from a platform-specific input source.\n */\nexport interface SourceMetadata {\n readonly username: string;\n readonly authorAssociation: string;\n readonly content: string;\n readonly sourceType: string;\n}\n\n/**\n * Adapter that extracts normalized metadata from platform-specific input.\n * Each platform (GitHub, GitLab, etc.) implements this interface.\n */\nexport interface ISourceAdapter {\n readonly platform: string;\n extractMetadata(input: unknown): SourceMetadata;\n}\n\n// ============================================================================\n// Stage Configuration\n// ============================================================================\n\n/**\n * Controls which pipeline stages run. Disabled stages use safe defaults.\n */\nexport const FirewallStagesSchema = z.object({\n sanitization: z.boolean().default(true),\n trustClassification: z.boolean().default(true),\n reputationAssessment: z.boolean().default(false),\n policyEnforcement: z.boolean().default(true),\n corroboration: z.boolean().default(false),\n audit: z.boolean().default(true),\n});\nexport type FirewallStages = z.infer<typeof FirewallStagesSchema>;\n\n/** Creates default stage configuration. */\nexport function createDefaultStages(): FirewallStages {\n return FirewallStagesSchema.parse({});\n}\n\n// ============================================================================\n// Firewall Configuration\n// ============================================================================\n\n/**\n * Configuration for the HostileInputFirewall.\n * The adapter is required; all other fields have sensible defaults.\n */\nexport const FirewallConfigSchema = z.object({\n stages: FirewallStagesSchema.default(() => ({\n sanitization: true,\n trustClassification: true,\n reputationAssessment: false,\n policyEnforcement: true,\n corroboration: false,\n audit: true,\n })),\n allowlistedMaintainers: z.array(z.string().min(1)).default([]),\n maxInputLength: z.number().int().positive().default(50_000),\n context: z\n .object({\n hasWriteAccess: z.boolean().default(false),\n hasSecretAccess: z.boolean().default(false),\n })\n .default(() => ({\n hasWriteAccess: false,\n hasSecretAccess: false,\n })),\n});\n\n/**\n * Full config including the adapter (not Zod-validated since it's an interface).\n */\nexport interface FirewallConfig {\n readonly adapter: ISourceAdapter;\n readonly stages?: Partial<FirewallStages>;\n readonly allowlistedMaintainers?: readonly string[];\n readonly maxInputLength?: number;\n readonly context?: {\n readonly hasWriteAccess?: boolean;\n readonly hasSecretAccess?: boolean;\n };\n /**\n * Rollout gate for behaviour changes to this published API (#5382).\n * Defaults to `NEXUS_FIREWALL_POLICY`, and to `off` when that is unset —\n * under `off` the firewall behaves exactly as it did before #5382.\n *\n * Explicit here as well as in the environment because the firewall is a\n * library: an embedding consumer must be able to opt in per instance without\n * setting a process-wide variable.\n */\n readonly policyMode?: FirewallPolicyMode;\n /**\n * Environment to resolve `policyMode` from when it is not given explicitly.\n * Injectable so the resolution path itself is testable — without this the\n * flag could be unreachable in production with every unit test still passing.\n */\n readonly env?: NodeJS.ProcessEnv;\n /**\n * Optional durable audit logger. When provided, every security decision the\n * firewall records is mirrored to this persistent, hash-chained store via\n * the audit bridge (#3291). When absent, decisions are in-memory only.\n */\n readonly auditLogger?: IAuditLogger;\n /**\n * Rollout gate for REPUTATION demotion (#5381). Distinct from `policyMode`:\n * different env var (`NEXUS_REPUTATION_GATING`) and — load-bearing — a\n * different default, `enforce` rather than `off`. Production reads this same\n * knob (`issue-triage.ts`, `pr-reviewer-helpers.ts`), and the firewall reading\n * a different one is what let the two compositions disagree under identical\n * configuration.\n */\n readonly reputationGatingMode?: ReputationGatingMode;\n /**\n * Supplies the reputation assessment. Defaults to `assessReputation` over the\n * instance's cache.\n *\n * Injectable for the same reason as `env` above: without it the reconciliation\n * is unobservable. The firewall hands the reputation engine only\n * `authorAssociation` + `injectionFlags` — the two inputs the trust classifier\n * already consumed — so reputation is never stricter than the classifier and\n * `reconcileTrustTier` returns the classifier tier every time. Deleting the\n * reconciliation passed 1588 tests (#5405). This seam is what lets a test\n * present a stricter tier and prove the check can fire.\n */\n readonly reputationAssessor?: (metadata: GitHubUserMetadata) => ReputationAssessment;\n /**\n * Whether the sanitizer's content tier is applied as a classification\n * downgrade (#4992). Default `true` — the firewall's behaviour since #826:\n * injection-bearing content from a Tier-2 author classifies as Tier 4.\n *\n * `false` keeps the classifier role-only. The sanitization stage still runs\n * and still records its injection flags — only the tier downgrade is\n * withheld. The dogfooding paths (`issue-triage`, `pr-reviewer`) set this\n * because they route content signals through reputation gating, which has\n * its own rollout knob (`NEXUS_REPUTATION_GATING`); applying the same signal\n * at classification too would bypass that knob and change the recorded\n * `trustTier` under the default `NEXUS_FIREWALL_POLICY=off`, which #5382\n * promises is pass-through.\n */\n readonly contentDowngrade?: boolean;\n}\n\n/**\n * Per-call inputs to {@link HostileInputFirewall.process} (#4992).\n *\n * These are the facts that vary by CALL rather than by instance, so a\n * process-wide firewall (the dogfooding singleton) can serve many repositories\n * and many access postures without holding any of them globally.\n */\nexport interface FirewallProcessOptions {\n /**\n * Maintainer allowlist for THIS call, from the repository context. Replaces —\n * does not merge with — the construction-time list, and is forgotten after\n * the call. When neither this nor the construction-time list was supplied,\n * no allowlist is consulted and `FirewallResult.isAllowlisted` is absent.\n */\n readonly allowlistedMaintainers?: readonly string[];\n /**\n * Access posture of the caller for THIS call, feeding the Rule-of-Two check.\n * Replaces the construction-time `context` for the call. Without it a shared\n * instance would evaluate every caller against one posture, and\n * `wouldRefuse` could never fire for a caller whose posture differs.\n */\n readonly context?: {\n readonly hasWriteAccess: boolean;\n readonly hasSecretAccess: boolean;\n };\n /**\n * The caller's own reputation measurement for THIS call. When present, the\n * reputation gate runs on it under `NEXUS_REPUTATION_GATING`, whether or not\n * the instance's `reputationAssessment` stage is on: `effectiveTrustTier` is\n * the enforced tier, `reputationGate` is returned, and the Rule-of-Two check,\n * `wouldRefuse` and the trust audit event all use that tier. This is what\n * lets a caller with richer signals than the firewall can see (account age,\n * comment history) act on ONE gate rather than two that can disagree.\n *\n * `assessment: undefined` means the caller measured nothing (reputation\n * disabled) but still wants the gate decision recorded on the classifier\n * tier; omitting the option entirely leaves the stage to the instance config.\n */\n readonly reputation?: {\n readonly assessment: ReputationAssessment | undefined;\n };\n}\n\n// ============================================================================\n// Agent Trust Label (ATL) Data\n// ============================================================================\n\n/**\n * Structured data for an Agent Trust Label.\n */\nexport const ATLDataSchema = z.object({\n tier: z.enum(['1', '2', '3', '4']),\n source: z.string().min(1),\n user: z.string().min(1),\n sanitized: z.boolean(),\n rep: z.number().min(0).max(1).optional(),\n});\nexport type ATLData = z.infer<typeof ATLDataSchema>;\n\n// ============================================================================\n// Firewall Error\n// ============================================================================\n\n/**\n * Error codes for firewall pipeline failures.\n */\nexport type FirewallErrorCode =\n | 'EXTRACTION_FAILED'\n | 'SANITIZATION_FAILED'\n | 'CLASSIFICATION_FAILED'\n | 'REPUTATION_FAILED'\n | 'INVALID_CONFIG'\n /**\n * #5382: a blocking policy violation refused the input outright, rather than\n * being surfaced as a signal on a successful result. Only reachable when the\n * firewall policy mode is `enforce` — under the default `off` a violation is\n * still returned via `ruleOfTwoViolation` on an `ok()` result.\n */\n | 'POLICY_REFUSED';\n\n/**\n * Structured error from the firewall pipeline.\n */\nexport interface FirewallError {\n readonly code: FirewallErrorCode;\n readonly message: string;\n readonly stage: string;\n}\n","/**\n * nexus-agents/security/firewall - Agent Trust Labels (ATL)\n *\n * Generates and parses structured trust labels that travel with\n * processed inputs through the agent pipeline. Format:\n * [ATL:tier=3,source=github-comment,user=octocat,sanitized=true,rep=0.45]\n *\n * @module security/firewall/agent-trust-labels\n * (Source: Issue #826 — Reusable Hostile Input Firewall)\n */\n\nimport type { ATLData } from './firewall-types.js';\nimport { ATLDataSchema } from './firewall-types.js';\n\n/** ATL prefix and suffix markers. */\nconst ATL_PREFIX = '[ATL:';\nconst ATL_SUFFIX = ']';\n\n/** Pattern to match and extract ATL strings. */\nconst ATL_PATTERN = /^\\[ATL:(.+)\\]$/;\n\n/**\n * Generates an Agent Trust Label string from structured data.\n *\n * @example\n * generateATL({ tier: '3', source: 'github-comment', user: 'octocat', sanitized: true })\n * // => \"[ATL:tier=3,source=github-comment,user=octocat,sanitized=true]\"\n */\nexport function generateATL(data: ATLData): string {\n const validated = ATLDataSchema.parse(data);\n const parts: string[] = [\n `tier=${validated.tier}`,\n `source=${encodeATLValue(validated.source)}`,\n `user=${encodeATLValue(validated.user)}`,\n `sanitized=${String(validated.sanitized)}`,\n ];\n\n if (validated.rep !== undefined) {\n parts.push(`rep=${validated.rep.toFixed(2)}`);\n }\n\n return `${ATL_PREFIX}${parts.join(',')}${ATL_SUFFIX}`;\n}\n\n/**\n * Parses an ATL string back into structured data.\n * Returns undefined if the string is not a valid ATL.\n */\nexport function parseATL(atl: string): ATLData | undefined {\n const match = ATL_PATTERN.exec(atl.trim());\n if (match === null) return undefined;\n\n const body = match[1];\n if (body === undefined) return undefined;\n\n const raw = parseKeyValuePairs(body);\n if (raw === undefined) return undefined;\n\n return validateParsedATL(raw);\n}\n\n/** Parses comma-separated key=value pairs into a record. */\nfunction parseKeyValuePairs(body: string): Record<string, string> | undefined {\n const pairs = body.split(',');\n const raw: Record<string, string> = {};\n for (const pair of pairs) {\n const eqIndex = pair.indexOf('=');\n if (eqIndex < 1) return undefined;\n raw[pair.slice(0, eqIndex)] = pair.slice(eqIndex + 1);\n }\n return raw;\n}\n\n/** Validates and converts raw key-value pairs to ATLData. */\nfunction validateParsedATL(raw: Record<string, string>): ATLData | undefined {\n const tier = raw['tier'];\n const source = raw['source'];\n const user = raw['user'];\n const sanitized = raw['sanitized'];\n if (tier === undefined || source === undefined || user === undefined || sanitized === undefined) {\n return undefined;\n }\n\n const parsed: ATLData = {\n tier: tier as ATLData['tier'],\n source: decodeATLValue(source),\n user: decodeATLValue(user),\n sanitized: sanitized === 'true',\n ...(raw['rep'] !== undefined ? { rep: parseFloat(raw['rep']) } : {}),\n };\n\n const result = ATLDataSchema.safeParse(parsed);\n return result.success ? result.data : undefined;\n}\n\n/** Encodes special characters in ATL values. */\nfunction encodeATLValue(value: string): string {\n return value.replace(/,/g, '%2C').replace(/=/g, '%3D').replace(/\\]/g, '%5D');\n}\n\n/** Decodes special characters in ATL values. */\nfunction decodeATLValue(value: string): string {\n return value.replace(/%2C/g, ',').replace(/%3D/g, '=').replace(/%5D/g, ']');\n}\n","/**\n * nexus-agents/security/firewall - Passthrough defaults for disabled stages\n *\n * Split out of the pipeline so it stays under the file-size lint (#4992).\n *\n * @module security/firewall/firewall-passthrough\n */\n\nimport { mapAuthorAssociation } from '../trust-classifier.js';\nimport type { ClassifyResult } from '../trust-classifier.js';\nimport { ROLE_DEFAULT_TRUST } from '../trust-types.js';\nimport type { SanitizedInput } from '../trust-types.js';\nimport type { SourceMetadata } from './firewall-types.js';\n\nexport function createPassthroughSanitized(meta: SourceMetadata): SanitizedInput {\n const userRole = mapAuthorAssociation(meta.authorAssociation);\n return {\n content: meta.content,\n originalLength: meta.content.length,\n trustTier: ROLE_DEFAULT_TRUST[userRole],\n contentTierMeasured: false,\n userRole,\n injectionFlags: [],\n strippedElements: [],\n truncated: false,\n wasModified: false,\n sanitizedAt: new Date().toISOString(),\n };\n}\n\nexport function createPassthroughClassification(meta: SourceMetadata): ClassifyResult {\n return {\n trustTier: '3',\n userRole: mapAuthorAssociation(meta.authorAssociation),\n isAllowlisted: false,\n wasDowngraded: false,\n reason: 'Trust classification disabled — default Tier 3',\n };\n}\n","/**\n * nexus-agents/security/firewall - Trust audit reason\n *\n * The trust audit event's `reason` must state what the reputation gate did —\n * or, under `audit`, what it would have done (#4992 review). A suppressed\n * demotion that leaves no trace reads as a clean classification.\n *\n * @module security/firewall/firewall-trust-reason\n */\n\nimport type { ReputationGateDecision } from '../reputation-model.js';\n\n/**\n * Appends the gate's effect to the classifier's reason.\n *\n * @param reason The classifier's own reason.\n * @param demoted Whether the enforced tier differs from the classifier tier.\n * @param gate The gate decision, or `undefined` when no gate ran.\n */\nexport function describeGate(\n reason: string,\n demoted: boolean,\n gate: ReputationGateDecision | undefined\n): string {\n if (gate === undefined) return reason;\n if (demoted) {\n return `${reason}; demoted to Tier ${gate.enforcedTier} by reputation gating (${gate.mode})`;\n }\n if (gate.demotionSuppressed) {\n return `${reason}; would demote to Tier ${gate.reconciledTier} (reputation gating: ${gate.mode})`;\n }\n return reason;\n}\n","/**\n * nexus-agents/security - Policy Gate\n *\n * Deterministic rule engine that validates all agent actions before\n * GitHub state mutations can occur. No LLM in the validation path.\n *\n * Defense layer 2 of the three-layer hardening architecture.\n * See: docs/architecture/UNTRUSTED_INPUT_HARDENING.md\n *\n * @module security/policy-gate\n * (Source: Issue #818, #822 — Phase 2: Policy Gate)\n */\n\nimport { z } from 'zod';\n\nimport type { AgentAction, AgentActionType, SourceCitation } from './action-schema.js';\nimport {\n isMutatingAction,\n isReadOnlyAction,\n requiresCitation,\n requiresHumanApproval,\n} from './action-schema.js';\nimport type { TrustTier } from './trust-types.js';\nimport { TRUST_TIER_NUMERIC } from './trust-types.js';\nimport { getRequiredTrustTier, canInfluenceDecisions } from './trust-classifier.js';\nimport type { AuditTrail } from './audit-trail.js';\nimport { emitPolicyEvent } from './audit-trail.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Violation detected by the policy gate.\n */\nexport const ViolationSchema = z.object({\n /** Machine-readable rule identifier. */\n rule: z.string().min(1),\n /** Human-readable description of the violation. */\n message: z.string().min(1),\n /** Severity: 'block' prevents execution, 'warn' logs only. */\n severity: z.enum(['block', 'warn']),\n});\nexport type Violation = z.infer<typeof ViolationSchema>;\n\n/**\n * Decision returned by the policy gate.\n */\nexport interface PolicyDecision {\n /** Whether the action is allowed to proceed. */\n readonly allowed: boolean;\n /** Whether human approval is required before execution. */\n readonly requiresApproval: boolean;\n /** All detected violations (blocking and warnings). */\n readonly violations: readonly Violation[];\n /** Timestamp of the evaluation (ISO 8601). */\n readonly evaluatedAt: string;\n}\n\n/**\n * Context for evaluating a policy decision.\n */\nexport interface ActionContext {\n /** Trust tier of the primary input source. */\n readonly inputTrustTier: TrustTier;\n /** Whether the agent currently has write access to the repository. */\n readonly hasWriteAccess: boolean;\n /** Whether the agent currently has access to secrets/tokens. */\n readonly hasSecretAccess: boolean;\n /** Set of labels that exist on the repository (for ProposeLabels validation). */\n readonly existingLabels?: ReadonlySet<string>;\n}\n\n// ============================================================================\n// Policy Rules\n// ============================================================================\n\n/** Extract sources from an action, handling actions that lack a sources field. */\nfunction getActionSources(action: AgentAction): readonly SourceCitation[] {\n if ('sources' in action) {\n return action.sources;\n }\n return [];\n}\n\n/** Check that citations exist for actions that require them. */\nfunction checkCitationRequirement(action: AgentAction): Violation | undefined {\n if (!requiresCitation(action.type)) return undefined;\n const sources = getActionSources(action);\n if (sources.length === 0) {\n return {\n rule: 'REQUIRE_CITATION',\n message: `Action '${action.type}' requires at least one source citation`,\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/** Check that the input trust tier meets action requirements. */\nfunction checkTrustRequirement(action: AgentAction, context: ActionContext): Violation | undefined {\n const requiredTier = getRequiredTrustTier(action.type);\n const requiredNumeric = TRUST_TIER_NUMERIC[requiredTier];\n const inputNumeric = TRUST_TIER_NUMERIC[context.inputTrustTier];\n\n if (inputNumeric > requiredNumeric) {\n return {\n rule: 'INSUFFICIENT_TRUST',\n message: `Action '${action.type}' requires Tier ${requiredTier} but input is Tier ${context.inputTrustTier}`,\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/** Check that Tier 3-4 input cannot drive decisions. */\nfunction checkInfluenceBlock(action: AgentAction, context: ActionContext): Violation | undefined {\n if (!canInfluenceDecisions(context.inputTrustTier) && isMutatingAction(action.type)) {\n return {\n rule: 'UNTRUSTED_INFLUENCE',\n message: `Tier ${context.inputTrustTier} input cannot drive mutating action '${action.type}'`,\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/**\n * Enforce the Rule of Two: no agent may simultaneously\n * (a) process untrusted input, (b) have write access, AND (c) access secrets.\n *\n * Exported (#3198) so the firewall's `policyEnforcement` stage can surface the\n * same assessment during input composition without duplicating the predicate.\n */\nexport function checkRuleOfTwo(context: ActionContext): Violation | undefined {\n const isUntrusted = TRUST_TIER_NUMERIC[context.inputTrustTier] >= 3;\n if (isUntrusted && context.hasWriteAccess && context.hasSecretAccess) {\n return {\n rule: 'RULE_OF_TWO',\n message:\n 'Rule of Two violation: agent simultaneously processes untrusted input, has write access, and accesses secrets',\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/**\n * Labels that GRANT PRIVILEGE or REDUCE SCRUTINY, and so may never be proposed\n * by an agent acting on external input (#4688).\n *\n * These are not ordinary taxonomy labels. In this repository they are control\n * inputs to CI:\n *\n * - `owner-ratified` — the label `check-governor-ratification.ts` accepts as\n * proof of owner ratification. Applying it BYPASSES the governance-of-the-\n * governor gate on `src/audit/`, `.rules/`, `CODEOWNERS` and friends.\n * - `skip-pr-review` — suppresses the PR review workflow.\n * - `pr-review-ci` — triggers the review workflow; less dangerous than the\n * two above, but still a workflow control rather than a description.\n *\n * Why this exists BEFORE it is needed: no production code applies a proposed\n * label today (`addLabels` has no non-test caller), so the path is latent. But\n * the consensus panel's dissenting seat was right that labels are not\n * zero-blast-radius, and the day someone wires triage output to `addLabels` is\n * the day an issue body can propose its own ratification. A guard added after\n * that wiring is a guard added after the incident.\n *\n * This is deliberately checked on the ACTION's effect rather than the author's\n * trust: an OWNER-authored body proposing `owner-ratified` is precisely the\n * self-modification the governor exists to prevent, so author trust must not\n * soften it.\n */\nconst PRIVILEGE_GRANTING_LABELS: ReadonlySet<string> = new Set([\n 'owner-ratified',\n 'skip-pr-review',\n 'pr-review-ci',\n]);\n\n/**\n * Refuse any proposal naming a privilege-granting label.\n *\n * Independent of {@link checkLabelValidity} on purpose: this check needs no\n * repository world state and reports privileged labels even when label\n * validity is unevaluated.\n */\nfunction checkPrivilegedLabels(action: AgentAction): Violation | undefined {\n if (action.type !== 'ProposeLabels') return undefined;\n\n // Case-insensitive, because the gate this protects is (#4689 follow-up):\n // `check-governor-ratification.ts` compares `l.toLowerCase()`, so\n // `Owner-Ratified` would satisfy ratification while slipping an exact-match\n // denylist. A guard must be at least as permissive in what it REJECTS as its\n // consumer is in what it ACCEPTS.\n const privileged = action.labels.filter((l) => PRIVILEGE_GRANTING_LABELS.has(l.toLowerCase()));\n if (privileged.length === 0) return undefined;\n\n return {\n rule: 'PRIVILEGED_LABEL',\n message:\n `Proposed labels grant privilege or reduce review scrutiny: ` +\n `${privileged.join(', ')}. These are CI control inputs, not descriptions, ` +\n `and are never proposable from agent-processed input (#4688).`,\n severity: 'block',\n };\n}\n\n/** Check that proposed labels exist in the repository's label set. */\nfunction checkLabelValidity(action: AgentAction, context: ActionContext): Violation | undefined {\n if (action.type !== 'ProposeLabels') return undefined;\n const labels = context.existingLabels;\n if (labels === undefined) {\n return {\n rule: 'LABEL_SET_UNAVAILABLE',\n message: 'repository label set not supplied; label validity unevaluated',\n severity: 'block',\n };\n }\n\n const invalid = action.labels.filter((l) => !labels.has(l));\n if (invalid.length > 0) {\n return {\n rule: 'INVALID_LABELS',\n message: `Proposed labels not in repository: ${invalid.join(', ')}`,\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/** Check that source citations meet trust requirements for the action. */\nfunction checkSourceTrustTiers(action: AgentAction): Violation | undefined {\n const sources = getActionSources(action);\n if (sources.length === 0) return undefined;\n\n const requiredTier = getRequiredTrustTier(action.type);\n const requiredNumeric = TRUST_TIER_NUMERIC[requiredTier];\n\n for (const source of sources) {\n if (source.type === 'issueComment') {\n const sourceTierNumeric = TRUST_TIER_NUMERIC[source.authorTrustTier];\n if (sourceTierNumeric > requiredNumeric) {\n return {\n rule: 'SOURCE_TRUST_MISMATCH',\n message: `Source from '${source.author}' (Tier ${source.authorTrustTier}) insufficient for action requiring Tier ${requiredTier}`,\n severity: 'warn',\n };\n }\n }\n }\n return undefined;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Evaluate an agent action against the policy gate.\n *\n * This is a deterministic check — no LLM in the loop. Returns a\n * PolicyDecision indicating whether the action is allowed, requires\n * human approval, or is blocked.\n *\n * @param action - The validated AgentAction to evaluate.\n * @param context - The current execution context.\n * @returns PolicyDecision with violations and approval requirements.\n */\nexport function evaluatePolicy(\n action: AgentAction,\n context: ActionContext,\n auditTrail?: AuditTrail\n): PolicyDecision {\n const violations: Violation[] = [];\n\n const checks = [\n checkCitationRequirement(action),\n checkTrustRequirement(action, context),\n checkInfluenceBlock(action, context),\n checkRuleOfTwo(context),\n checkLabelValidity(action, context),\n checkPrivilegedLabels(action),\n checkSourceTrustTiers(action),\n ];\n\n for (const violation of checks) {\n if (violation !== undefined) {\n violations.push(violation);\n }\n }\n\n const hasBlockingViolation = violations.some((v) => v.severity === 'block');\n // #4463: gate on irreversibility + external visibility, not on mutation as\n // such. ProposeLabels and GeneratePatchPlan are reversible and internal, and\n // reach here having already cleared citation, trust-tier, influence-block,\n // Rule-of-Two and label-validity checks. DraftReply still gates because it\n // publishes text under the project's identity on a surface others read.\n // `isMutatingAction` stays broad above (line ~113) for the influence block:\n // low-trust input must not drive ANY mutating action, approved or not.\n const needsApproval = !hasBlockingViolation && requiresHumanApproval(action.type);\n\n const decision: PolicyDecision = {\n allowed: !hasBlockingViolation,\n requiresApproval: needsApproval,\n violations,\n evaluatedAt: new Date().toISOString(),\n };\n\n // #3191: when an audit trail is supplied, record the decision so policy\n // outcomes are part of the durable audit record (the gate previously emitted\n // nothing). Optional — pure callers pass no trail and incur no side effect.\n if (auditTrail !== undefined) {\n emitPolicyEvent(auditTrail, {\n actionType: action.type,\n allowed: decision.allowed,\n requiresApproval: decision.requiresApproval,\n inputTrustTier: context.inputTrustTier,\n violationRules: violations.map((v) => v.rule),\n });\n }\n\n return decision;\n}\n\n/**\n * Quick check: can this action type proceed at all given the input trust tier?\n * Useful for early rejection before full policy evaluation.\n */\nexport function canProceed(actionType: AgentActionType, inputTrustTier: TrustTier): boolean {\n if (isReadOnlyAction(actionType)) {\n const requiredTier = getRequiredTrustTier(actionType);\n return TRUST_TIER_NUMERIC[inputTrustTier] <= TRUST_TIER_NUMERIC[requiredTier];\n }\n return canInfluenceDecisions(inputTrustTier);\n}\n","/**\n * nexus-agents/security - Typed Action Schema\n *\n * Zod schemas and TypeScript types for the typed action constraint system.\n * Agents processing untrusted GitHub input MUST emit only predefined typed\n * actions (never free-form tool calls). This module defines those action\n * schemas, validates them at runtime, and classifies them as read-only or\n * mutating for the policy gate.\n *\n * @module security/action-schema\n * (Source: Issue #818, #820)\n */\n\nimport { z } from 'zod';\n\nimport { TrustTierSchema } from './trust-types.js';\n\n// ============================================================================\n// Result Type (local — avoids circular dependency with core/types)\n// ============================================================================\n\n/** Validation result using the project Result pattern. */\nexport type ActionValidationResult =\n { ok: true; value: AgentAction } | { ok: false; error: string };\n\n// ============================================================================\n// Source Citations\n// ============================================================================\n\n/** Source citation from a file tracked in the repository. */\nconst RepoFileSource = z.object({\n type: z.literal('repoFile'),\n path: z.string().min(1),\n line: z.number().int().positive().optional(),\n commit: z\n .string()\n .regex(/^[a-f0-9]{7,40}$/)\n .optional(),\n});\n\n/** Source citation from a GitHub issue comment. */\nconst IssueCommentSource = z.object({\n type: z.literal('issueComment'),\n issueNumber: z.number().int().positive(),\n commentId: z.number().int().positive(),\n author: z.string().min(1),\n authorTrustTier: TrustTierSchema,\n});\n\n/**\n * Source citation from the body of a GitHub issue (#4667).\n *\n * Triage previously cited the issue as a `repoFile` with a synthesised path\n * (`issues/42`). That is not a repo file, and the mislabel is why corroboration\n * always passed: `hasSourceAtTier` treats repo files as Tier 1 unconditionally,\n * so untrusted issue text was corroborating actions at maintainer trust.\n *\n * Modelled on `IssueCommentSource` — it carries the author and their tier, so\n * the trust of the content travels with the citation. It is deliberately NOT an\n * `issueComment`: that requires a `commentId`, and faking one to fix a mislabel\n * would trade an honest error for a dishonest one.\n */\nconst IssueBodySource = z.object({\n type: z.literal('issueBody'),\n issueNumber: z.number().int().positive(),\n author: z.string().min(1),\n authorTrustTier: TrustTierSchema,\n});\n\n/** Source citation from a CI pipeline result. */\nconst CIResultSource = z.object({\n type: z.literal('ciResult'),\n runId: z.number().int().positive(),\n status: z.enum(['pass', 'fail']),\n job: z.string().min(1),\n});\n\n/** Source citation from a policy or governance document. */\nconst PolicyDocSource = z.object({\n type: z.literal('policyDoc'),\n path: z.string().min(1),\n section: z.string().min(1),\n});\n\n/** Source citation from a maintainer slash-command or explicit instruction. */\nconst MaintainerCommandSource = z.object({\n type: z.literal('maintainerCommand'),\n username: z.string().min(1),\n commentId: z.number().int().positive(),\n});\n\n/**\n * Discriminated union of all valid source citation types.\n * Every decision-making action MUST cite at least one source.\n */\nexport const SourceCitationSchema = z.discriminatedUnion('type', [\n IssueBodySource,\n RepoFileSource,\n IssueCommentSource,\n CIResultSource,\n PolicyDocSource,\n MaintainerCommandSource,\n]);\n\n/** Inferred TypeScript type for a source citation. */\nexport type SourceCitation = z.infer<typeof SourceCitationSchema>;\n\n// ============================================================================\n// Typed Actions\n// ============================================================================\n\n/** Read-only analysis action: summarize an issue with citations. */\nconst SummarizeIssueAction = z.object({\n type: z.literal('SummarizeIssue'),\n summary: z.string().min(10).max(2000),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Suggest labels for an issue (max 5, must match existing label set). */\nconst ProposeLabelsAction = z.object({\n type: z.literal('ProposeLabels'),\n labels: z.array(z.string()).min(1).max(5),\n reason: z.string().min(10).max(500),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Draft a reply comment (always requires human approval before posting). */\nexport const DRAFT_REPLY_BODY_MAX_LENGTH = 2000;\n\nconst DraftReplyAction = z.object({\n type: z.literal('DraftReply'),\n body: z.string().min(10).max(DRAFT_REPLY_BODY_MAX_LENGTH),\n requiresApproval: z.literal(true),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Explicit escalation to a human maintainer with reason and context. */\nconst RequestHumanApprovalAction = z.object({\n type: z.literal('RequestHumanApproval'),\n reason: z.string().min(10).max(500),\n context: z.string().min(10).max(2000),\n});\n\n/**\n * Propose a set of file modifications (requires maintainer corroboration).\n * Forbidden from Tier 3-4 input without maintainer corroboration.\n */\nconst GeneratePatchPlanAction = z.object({\n type: z.literal('GeneratePatchPlan'),\n files: z\n .array(\n z.object({\n path: z.string().min(1),\n operation: z.enum(['modify', 'create', 'delete']),\n description: z.string().min(10).max(500),\n })\n )\n .min(1)\n .max(10),\n rationale: z.string().min(10).max(1000),\n requiresApproval: z.literal(true),\n sources: z.array(SourceCitationSchema).min(2).max(20),\n});\n\n/** Categorize an issue by type with confidence score. */\nconst ClassifyIssueAction = z.object({\n type: z.literal('ClassifyIssue'),\n category: z.enum(['bug', 'feature', 'question', 'documentation', 'security', 'performance']),\n confidence: z.number().min(0).max(1),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Identify potential duplicate issues with similarity scores. */\nconst IdentifyDuplicatesAction = z.object({\n type: z.literal('IdentifyDuplicates'),\n candidates: z.array(z.number().int().positive()).min(1).max(10),\n similarity: z.array(z.number().min(0).max(1)),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Explicit refusal to act, with escalation target. */\nconst RefuseActionAction = z.object({\n type: z.literal('RefuseAction'),\n reason: z.string().min(10).max(500),\n escalateTo: z.enum(['maintainer', 'security']),\n});\n\n/** Handoff delegation from one agent to another by capability. (Issue #834) */\nconst HandoffMessageAction = z.object({\n type: z.literal('HandoffMessage'),\n targetCapability: z.string().min(1).max(100),\n reason: z.string().min(5).max(500),\n inputTrustTier: z.enum(['1', '2', '3', '4']),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/**\n * Discriminated union of all valid agent actions.\n * This is the ONLY schema agents may emit when processing untrusted input.\n */\nexport const AgentActionSchema = z.discriminatedUnion('type', [\n SummarizeIssueAction,\n ProposeLabelsAction,\n DraftReplyAction,\n RequestHumanApprovalAction,\n GeneratePatchPlanAction,\n ClassifyIssueAction,\n IdentifyDuplicatesAction,\n RefuseActionAction,\n HandoffMessageAction,\n]);\n\n/** Inferred TypeScript type for an agent action. */\nexport type AgentAction = z.infer<typeof AgentActionSchema>;\n\n/** All valid action type discriminator values. */\nexport type AgentActionType = AgentAction['type'];\n\n// ============================================================================\n// Action Classification\n// ============================================================================\n\n/**\n * Action types that are read-only and do not modify GitHub state.\n * These can be executed without human approval (subject to policy gate).\n */\nconst READ_ONLY_ACTIONS: ReadonlySet<AgentActionType> = new Set<AgentActionType>([\n 'SummarizeIssue',\n 'ClassifyIssue',\n 'IdentifyDuplicates',\n 'RefuseAction',\n 'RequestHumanApproval',\n 'HandoffMessage',\n]);\n\n/**\n * Action types that can modify GitHub state (labels, comments, code).\n *\n * Used for the untrusted-input influence block: low-trust input must not drive\n * ANY of these, approved or not. NOT the approval set — see\n * {@link APPROVAL_REQUIRED_ACTIONS}, which is narrower (#4463).\n */\nconst MUTATING_ACTIONS: ReadonlySet<AgentActionType> = new Set<AgentActionType>([\n 'ProposeLabels',\n 'DraftReply',\n 'GeneratePatchPlan',\n]);\n\n/**\n * Action types that MUST include at least one source citation.\n * Escalation and refusal actions are exempt (they report inability to act).\n */\nconst CITATION_REQUIRED_ACTIONS: ReadonlySet<AgentActionType> = new Set<AgentActionType>([\n 'SummarizeIssue',\n 'ProposeLabels',\n 'DraftReply',\n 'GeneratePatchPlan',\n 'ClassifyIssue',\n 'IdentifyDuplicates',\n 'HandoffMessage',\n]);\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Validate an unknown value against the AgentActionSchema.\n * Returns a Result: `{ ok: true; value }` on success or `{ ok: false; error }` on failure.\n *\n * @param input - The value to validate (typically parsed JSON from an agent).\n * @returns Validation result following the project Result pattern.\n */\nexport function validateAgentAction(input: unknown): ActionValidationResult {\n const result = AgentActionSchema.safeParse(input);\n if (result.success) {\n return { ok: true, value: result.data };\n }\n const messages = result.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);\n return { ok: false, error: `Action validation failed: ${messages.join('; ')}` };\n}\n\n/**\n * Check whether an action type is read-only (does not modify GitHub state).\n *\n * @param actionType - The action type discriminator value.\n * @returns True if the action is read-only.\n */\nexport function isReadOnlyAction(actionType: AgentActionType): boolean {\n return READ_ONLY_ACTIONS.has(actionType);\n}\n\n/**\n * Check whether an action type can modify GitHub state.\n * Mutating actions always require human approval before execution.\n *\n * @param actionType - The action type discriminator value.\n * @returns True if the action can modify state.\n */\nexport function isMutatingAction(actionType: AgentActionType): boolean {\n return MUTATING_ACTIONS.has(actionType);\n}\n\n/**\n * Action types that still require human approval after every policy check has\n * passed (#4463).\n *\n * Deliberately narrower than {@link MUTATING_ACTIONS}. Approval is a gate on\n * IRREVERSIBILITY and EXTERNAL VISIBILITY, not on mutation as such:\n *\n * - `ProposeLabels` — reversible in one click, internal to the tracker, and its\n * schema carries no `requiresApproval` field. Not gated: it reaches this point\n * having already passed citation, trust-tier, influence-block, Rule-of-Two and\n * label-validity checks, so pausing bought delay, not safety.\n * - `DraftReply` — **publishes text under the project's identity**, on a surface\n * other people read. Untrusted issue/PR content is in scope by definition\n * here, so a prompt-injected reply is attacker-authored text posted publicly.\n * Deleting it afterwards does not un-publish it.\n * - `GeneratePatchPlan` — gated, and deliberately so: its own schema encodes\n * `requiresApproval: z.literal(true)` alongside a two-source corroboration\n * minimum (`sources.min(2)`). Dropping the gate here while the schema still\n * mandates the flag would put two sources of truth in conflict, and the\n * two-factor design is intentional for an action that proposes file writes\n * from potentially untrusted input.\n */\nconst APPROVAL_REQUIRED_ACTIONS: ReadonlySet<AgentActionType> = new Set<AgentActionType>([\n 'DraftReply',\n 'GeneratePatchPlan',\n]);\n\n/**\n * Whether an action needs human approval even after passing every policy check.\n *\n * Distinct from {@link isMutatingAction}, which stays broad because it also\n * drives the untrusted-input influence block — low-trust input must not be able\n * to drive ANY mutating action, approved or not.\n *\n * @param actionType - The action type discriminator value.\n * @returns True if a human must approve before execution.\n */\nexport function requiresHumanApproval(actionType: AgentActionType): boolean {\n return APPROVAL_REQUIRED_ACTIONS.has(actionType);\n}\n\n/**\n * Check whether an action type requires at least one source citation.\n * Escalation (RequestHumanApproval) and refusal (RefuseAction) are exempt.\n *\n * @param actionType - The action type discriminator value.\n * @returns True if the action must include source citations.\n */\nexport function requiresCitation(actionType: AgentActionType): boolean {\n return CITATION_REQUIRED_ACTIONS.has(actionType);\n}\n","/**\n * nexus-agents/security - Corroboration Validator\n *\n * Validates that agent decisions are backed by sufficient evidence from\n * authoritative sources. Each action type has specific corroboration\n * requirements (e.g., closing issues requires CI pass or maintainer comment).\n *\n * Defense layer 3 of the three-layer hardening architecture.\n * See: docs/architecture/UNTRUSTED_INPUT_HARDENING.md\n *\n * @module security/corroboration-validator\n * (Source: Issue #818, #823 — Phase 2: Corroboration Validator)\n */\n\nimport type { AgentAction, AgentActionType, SourceCitation } from './action-schema.js';\nimport type { TrustTier } from './trust-types.js';\nimport { TRUST_TIER_NUMERIC } from './trust-types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Result of corroboration validation.\n */\nexport interface CorroborationResult {\n /** Whether corroboration requirements are satisfied. */\n readonly satisfied: boolean;\n /** Sources that contributed to corroboration. */\n readonly corroboratingSources: readonly SourceCitation[];\n /** Missing corroboration requirements (empty when satisfied). */\n readonly missing: readonly string[];\n /** Action type that was validated. */\n readonly actionType: AgentActionType;\n}\n\n/**\n * Rule defining what corroboration an action requires.\n */\nexport interface CorroborationRule {\n /** Human-readable description of what's required. */\n readonly description: string;\n /** Predicate: does this set of sources satisfy the requirement? */\n readonly isSatisfied: (sources: readonly SourceCitation[]) => boolean;\n}\n\n// ============================================================================\n// Source Helpers\n// ============================================================================\n\n/** Check if sources include a passing CI result. */\nfunction hasCIPass(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'ciResult' && s.status === 'pass');\n}\n\n/** Check if sources include a maintainer command. */\nfunction hasMaintainerCommand(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'maintainerCommand');\n}\n\n/** Check if sources include a Tier 1 issue comment. */\nfunction hasTier1Comment(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'issueComment' && s.authorTrustTier === '1');\n}\n\n/** Check if sources include a repo file reference. */\nfunction hasRepoFileRef(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'repoFile');\n}\n\n/** Check if sources include a repo file with line reference. */\nfunction hasCodeLevelEvidence(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'repoFile' && s.line !== undefined);\n}\n\n/** Check if sources include a policy document reference. */\nfunction hasPolicyDocRef(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'policyDoc');\n}\n\n/**\n * True when an ISSUE BODY source is authored at or above `maxTier` (#4667).\n *\n * Deliberately narrower than {@link hasSourceAtTier}, which returns `true` for\n * any non-authored source — repo files, CI results and policy docs are all\n * Tier 1 there. Reusing it here made a CI result alone satisfy ProposeLabels,\n * which the corroboration tests correctly forbid.\n */\nfunction hasIssueBodyAtTier(sources: readonly SourceCitation[], maxTier: TrustTier): boolean {\n const maxNumeric = TRUST_TIER_NUMERIC[maxTier];\n return sources.some(\n (s) => s.type === 'issueBody' && TRUST_TIER_NUMERIC[s.authorTrustTier] <= maxNumeric\n );\n}\n\n/** Check if at least one source meets a minimum trust tier. */\nfunction hasSourceAtTier(sources: readonly SourceCitation[], maxTier: TrustTier): boolean {\n const maxNumeric = TRUST_TIER_NUMERIC[maxTier];\n return sources.some((s) => {\n // Author-attributed content carries its author's trust, not the repo's.\n // `issueBody` was previously cited as a `repoFile` and so counted as Tier 1\n // unconditionally — untrusted issue text corroborating at maintainer trust\n // (#4667).\n if (s.type === 'issueComment' || s.type === 'issueBody') {\n return TRUST_TIER_NUMERIC[s.authorTrustTier] <= maxNumeric;\n }\n // Repo files, CI results, policy docs, maintainer commands = Tier 1\n return true;\n });\n}\n\n// ============================================================================\n// Per-Action Corroboration Rules\n// ============================================================================\n\n/**\n * Corroboration rules for each action type.\n * See CLAUDE.md \"Corroboration Requirements\" table.\n */\nconst ACTION_CORROBORATION_RULES: Readonly<Record<AgentActionType, readonly CorroborationRule[]>> =\n {\n SummarizeIssue: [\n {\n description: 'At least one Tier 1/2 source',\n isSatisfied: (s) => hasSourceAtTier(s, '2'),\n },\n ],\n ProposeLabels: [\n {\n // #4667: the old description read \"issue body (repo file)\" — the rule\n // was written when triage cited the issue as a `repoFile`, so an issue\n // body satisfied it unconditionally regardless of who wrote it. Now\n // that the citation is honest (`issueBody`, carrying its author's\n // tier), the rule has to say what it always meant: an issue body\n // corroborates a label proposal only from a sufficiently trusted\n // author. Dropping the clause entirely would make this rule\n // unsatisfiable for every author including the repo owner — trading a\n // check that could never fail for one that can never pass.\n description:\n 'Issue body from a Tier 1/2 author, OR repo file / maintainer instruction / policy doc',\n isSatisfied: (s) =>\n hasIssueBodyAtTier(s, '2') ||\n hasRepoFileRef(s) ||\n hasMaintainerCommand(s) ||\n hasPolicyDocRef(s),\n },\n ],\n DraftReply: [\n {\n description: 'At least one Tier 1 source citation',\n isSatisfied: (s) =>\n hasRepoFileRef(s) ||\n hasCIPass(s) ||\n hasMaintainerCommand(s) ||\n hasPolicyDocRef(s) ||\n hasTier1Comment(s),\n },\n ],\n GeneratePatchPlan: [\n {\n description: 'Failing test OR bug reproduction steps (code-level evidence)',\n isSatisfied: (s) => hasCodeLevelEvidence(s) || hasCIPass(s),\n },\n {\n description: 'Maintainer corroboration',\n isSatisfied: (s) => hasMaintainerCommand(s) || hasTier1Comment(s),\n },\n ],\n ClassifyIssue: [\n {\n description: 'At least one source citation',\n isSatisfied: (s) => s.length > 0,\n },\n ],\n IdentifyDuplicates: [\n {\n description: 'At least one source citation',\n isSatisfied: (s) => s.length > 0,\n },\n ],\n RequestHumanApproval: [],\n RefuseAction: [],\n HandoffMessage: [\n {\n description: 'At least one source citation for trust tier propagation',\n isSatisfied: (s) => s.length > 0,\n },\n ],\n };\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Validate that an agent action has sufficient corroboration from\n * authoritative sources.\n *\n * @param action - The validated AgentAction to check.\n * @returns CorroborationResult indicating whether requirements are met.\n */\nexport function validateCorroboration(action: AgentAction): CorroborationResult {\n const rules = ACTION_CORROBORATION_RULES[action.type];\n const sources: readonly SourceCitation[] = 'sources' in action ? action.sources : [];\n\n if (rules.length === 0) {\n return {\n satisfied: true,\n corroboratingSources: sources,\n missing: [],\n actionType: action.type,\n };\n }\n\n const missing: string[] = [];\n const corroborating: SourceCitation[] = [];\n\n for (const rule of rules) {\n if (rule.isSatisfied(sources)) {\n for (const s of sources) {\n if (!corroborating.includes(s)) {\n corroborating.push(s);\n }\n }\n } else {\n missing.push(rule.description);\n }\n }\n\n return {\n satisfied: missing.length === 0,\n corroboratingSources: corroborating,\n missing,\n actionType: action.type,\n };\n}\n\n/**\n * Get the corroboration rules for an action type.\n * Useful for displaying requirements to users.\n */\nexport function getCorroborationRules(actionType: AgentActionType): readonly CorroborationRule[] {\n return ACTION_CORROBORATION_RULES[actionType];\n}\n","/**\n * nexus-agents/security/firewall - Pipeline Engine\n *\n * HostileInputFirewall: a composition layer that orchestrates existing\n * security modules (sanitizer, trust-classifier, reputation-model,\n * audit-trail) into a configurable, source-agnostic pipeline.\n *\n * Replaces ad-hoc manual composition found in issue-triage, pr-reviewer,\n * and secure-handler with a single reusable abstraction.\n *\n * @module security/firewall/firewall-pipeline\n * (Source: Issue #826 — Reusable Hostile Input Firewall)\n */\n\nimport type { Result } from '../../core/result.js';\nimport { err, ok } from '../../core/result.js';\nimport { resolveFirewallPolicyMode, type FirewallPolicyMode } from './firewall-policy-mode.js';\nimport {\n AuditTrail,\n createAuditTrail,\n emitReputationEvent,\n emitSanitizationEvent,\n MAX_STRIPPED_ELEMENTS_PER_EVENT,\n emitTrustEvent,\n} from '../audit-trail.js';\nimport { createDurableAuditSink } from '../audit-bridge.js';\nimport { sanitizeInput } from '../input-sanitizer.js';\nimport type {\n ReputationAssessment,\n GitHubUserMetadata,\n ReputationGatingMode,\n} from '../reputation-model.js';\nimport {\n assessReputation,\n ReputationCache,\n gateWithReputation,\n resolveReputationGatingMode,\n} from '../reputation-model.js';\nimport type { ReputationGateDecision } from '../reputation-model.js';\nimport type { ClassifyResult } from '../trust-classifier.js';\nimport { classifyTrust, mapAuthorAssociation } from '../trust-classifier.js';\nimport type { SanitizedInput, TrustTier } from '../trust-types.js';\nimport { generateATL } from './agent-trust-labels.js';\nimport {\n createPassthroughClassification,\n createPassthroughSanitized,\n} from './firewall-passthrough.js';\nimport { describeGate } from './firewall-trust-reason.js';\nimport type {\n ATLData,\n FirewallConfig,\n FirewallError,\n FirewallProcessOptions,\n FirewallStages,\n SourceMetadata,\n} from './firewall-types.js';\nimport { FirewallConfigSchema } from './firewall-types.js';\nimport { checkRuleOfTwo } from '../policy-gate.js';\nimport type { Violation } from '../policy-gate.js';\nimport { validateCorroboration } from '../corroboration-validator.js';\nimport type { AgentAction, SourceCitation } from '../action-schema.js';\nimport { createLogger } from '../../core/index.js';\n\nconst logger = createLogger({ component: 'HostileInputFirewall' });\n\n// ============================================================================\n// Firewall Result\n// ============================================================================\n\n/**\n * Output of the firewall pipeline. Aggregates results from each stage.\n */\nexport interface FirewallResult {\n readonly sanitized: SanitizedInput;\n readonly trust: ClassifyResult;\n /**\n * Whether the author is on the maintainer allowlist — present ONLY when an\n * allowlist was consulted (#4992), i.e. one was supplied at construction or\n * per call. `trust.isAllowlisted` is the classifier's published always-boolean\n * field and reads `false` whether the list was empty or never supplied; this\n * field is the one to record, because absence here means \"not measured\"\n * rather than \"measured false\" — the same treatment `reputationGate` gets.\n */\n readonly isAllowlisted?: boolean;\n readonly reputation?: ReputationAssessment;\n /**\n * The tier consumers should ENFORCE on (#3106): the classifier tier\n * reconciled with the reputation assessment (demotion-only; Tier-1/allowlist\n * wins; equals `trust.trustTier` when reputation is absent). Previously the\n * reputation tier was computed but dropped — `trust.trustTier` alone left\n * reputation unenforced.\n */\n readonly effectiveTrustTier: TrustTier;\n /**\n * The reputation gating decision behind `effectiveTrustTier` (#5381).\n *\n * **Absent means the reputation stage did not run** — not \"it ran and\n * suppressed nothing\". `ReputationGateDecision.demotionSuppressed` is a\n * required boolean, so surfacing it unconditionally would report `false` for a\n * check that never happened. Since the stage defaults to off, that\n * unevaluated case is the common one.\n */\n readonly reputationGate?: ReputationGateDecision;\n readonly atl: string;\n /**\n * Rule-of-Two assessment surfaced by the `policyEnforcement` stage (#3198):\n * present (with `severity: 'block'`) when the effective tier is untrusted AND\n * the configured context has both write and secret access. The firewall is a\n * signal provider — it SURFACES this for the consumer to enforce; it does not\n * hard-block. `undefined` when the stage is disabled or the rule holds.\n */\n readonly ruleOfTwoViolation?: Violation;\n /**\n * The rollout mode this run was evaluated under (#5382). Recorded on the\n * result rather than left implicit so a consumer reading a verdict can tell\n * WHICH policy produced it — a result that does not say which rules were in\n * force cannot be audited later.\n */\n readonly policyMode: FirewallPolicyMode;\n /**\n * Whether `enforce` would have refused this input.\n *\n * This is what makes `audit` mode measurable, and it is the field that makes\n * the mode a real gate rather than a switch with two indistinguishable\n * settings: under `audit` the answer is computed and reported while the input\n * is still allowed through, so an operator can size the impact of flipping to\n * `enforce` before flipping it.\n *\n * Always `false` under `enforce`, because an input that would be refused IS\n * refused — it comes back as a `POLICY_REFUSED` error, not a result.\n */\n readonly wouldRefuse: boolean;\n readonly auditEvents: readonly { readonly id: string; readonly type: string }[];\n /**\n * Whether a durable `AuditLogger` was configured for this instance (#4992\n * review). `configured` means this run's events were HANDED to that logger;\n * delivery to the hash chain is subject to the logger's own severity filter\n * (trust events are `info`), its bounded queue and its timed, fail-loud\n * flush, and is NOT confirmed per call — the write is queued. `none` means\n * the events exist only in the in-memory trail, which the next `process()`\n * call clears. This is a construction-time fact, not a per-call outcome.\n */\n readonly auditSink: 'configured' | 'none';\n readonly durationMs: number;\n}\n\n/**\n * Outcome of {@link HostileInputFirewall.validateAction} (#5382).\n *\n * A discriminated union rather than a struct with optional fields, deliberately:\n * a caller cannot read `satisfied` without first narrowing on `evaluated`, so\n * \"the stage did not run\" is structurally impossible to misread as \"the stage\n * ran and passed\". `stages.corroboration` defaults to `false`, which makes the\n * unevaluated branch the COMMON case — exactly where a silent `satisfied: true`\n * would do the most damage.\n */\nexport type ActionValidation =\n | {\n readonly evaluated: false;\n /** Why no verdict exists. Absence is attributable, not anonymous. */\n readonly reason: 'corroboration-stage-disabled';\n readonly policyMode: FirewallPolicyMode;\n }\n | {\n readonly evaluated: true;\n readonly satisfied: boolean;\n /** Unmet corroboration requirements; empty when satisfied. */\n readonly missing: readonly string[];\n readonly corroboratingSources: readonly SourceCitation[];\n readonly policyMode: FirewallPolicyMode;\n /** Whether `enforce` would have refused this action (see FirewallResult). */\n readonly wouldRefuse: boolean;\n };\n\n// ============================================================================\n// HostileInputFirewall\n// ============================================================================\n\n/**\n * Orchestrates existing security modules into a configurable pipeline.\n * Each stage is independently toggleable via config.stages.\n */\nexport class HostileInputFirewall {\n private readonly stages: FirewallStages;\n /**\n * Construction-time allowlist, or `undefined` when none was supplied (#4992).\n * The Zod default of `[]` is deliberately NOT taken here: an absent list and\n * an empty list must stay distinguishable so `isAllowlisted` can be omitted\n * rather than recorded as `false` when nothing was consulted.\n */\n private readonly allowlisted: readonly string[] | undefined;\n private readonly maxInputLength: number;\n private readonly adapter: FirewallConfig['adapter'];\n private readonly reputationCache: ReputationCache;\n private readonly auditTrail: AuditTrail;\n private readonly context: { readonly hasWriteAccess: boolean; readonly hasSecretAccess: boolean };\n /** #5382 rollout gate; resolved once at construction, not per call. */\n private readonly policyMode: FirewallPolicyMode;\n /** #5381 reputation-demotion gate; a DIFFERENT knob from `policyMode`. */\n private readonly reputationGatingMode: ReputationGatingMode;\n /** #5405 seam: how a reputation assessment is obtained. */\n private readonly assessReputationFn: (metadata: GitHubUserMetadata) => ReputationAssessment;\n /** #4992: whether the sanitizer's content tier downgrades the classifier tier. */\n private readonly contentDowngrade: boolean;\n /** #4992 review: whether a durable logger was configured (not per-call delivery). */\n private readonly auditSink: 'configured' | 'none';\n\n constructor(config: FirewallConfig) {\n const validated = FirewallConfigSchema.parse({\n stages: config.stages,\n allowlistedMaintainers: config.allowlistedMaintainers,\n maxInputLength: config.maxInputLength,\n context: config.context,\n });\n this.stages = validated.stages;\n this.allowlisted =\n config.allowlistedMaintainers !== undefined ? validated.allowlistedMaintainers : undefined;\n this.maxInputLength = validated.maxInputLength;\n this.adapter = config.adapter;\n this.reputationCache = new ReputationCache();\n // Mirror security decisions to the durable, hash-chained audit log when a\n // logger is provided; otherwise stay in-memory only (#3291).\n this.auditTrail = createAuditTrail(\n config.auditLogger !== undefined ? createDurableAuditSink(config.auditLogger) : undefined\n );\n this.auditSink = config.auditLogger !== undefined ? 'configured' : 'none';\n this.context = validated.context;\n // Explicit config wins; otherwise the environment; otherwise `off`. Resolved\n // once here rather than per `process()` call so a mid-run env change cannot\n // make two inputs in the same batch answer to different policies.\n this.policyMode = config.policyMode ?? resolveFirewallPolicyMode(config.env ?? process.env);\n // Same resolve-once discipline, but a SEPARATE knob: `NEXUS_REPUTATION_GATING`,\n // defaulting to `enforce`. Production reads this one, so the firewall reading\n // it too is what makes the two compositions agree under one configuration.\n this.reputationGatingMode =\n config.reputationGatingMode ?? resolveReputationGatingMode(config.env ?? process.env);\n this.assessReputationFn =\n config.reputationAssessor ?? ((metadata) => assessReputation(metadata, this.reputationCache));\n this.contentDowngrade = config.contentDowngrade ?? true;\n }\n\n /**\n * Processes untrusted input through the firewall pipeline.\n * Returns a structured FirewallResult or a typed FirewallError.\n *\n * `options` carries the per-call facts (#4992): the repository's maintainer\n * allowlist and the caller's access posture. Each replaces its\n * construction-time counterpart for this call only, so one shared instance\n * never holds a repository's allowlist or a caller's posture process-wide.\n */\n process(input: unknown, options?: FirewallProcessOptions): Result<FirewallResult, FirewallError> {\n const start = Date.now();\n this.auditTrail.clear();\n const allowlist = options?.allowlistedMaintainers ?? this.allowlisted;\n const context = options?.context ?? this.context;\n\n // Stage 1: Extract metadata via adapter\n const metaResult = this.runExtraction(input);\n if (!metaResult.ok) return metaResult;\n const meta = metaResult.value;\n\n // Stage 2: Sanitize input\n const sanitized = this.runSanitization(meta, allowlist);\n\n // Stage 3: Classify trust\n const trust = this.runClassification(meta, sanitized, allowlist);\n\n // Stage 4: Assess reputation — the caller's when supplied, else the stage.\n const reputation = this.runReputation(meta, sanitized, options?.reputation);\n\n const { tier: effectiveTrustTier, gate: reputationGate } = this.runReputationGate(\n trust.trustTier,\n reputation,\n options?.reputation !== undefined\n );\n\n // Trust event on the tier the consumer acts on, not the pre-reputation one.\n this.recordTrustDecision(meta, trust, effectiveTrustTier, reputationGate, allowlist);\n\n // Stage 5: Generate ATL (labelled with the enforced tier)\n const atl = this.buildATL(meta, effectiveTrustTier, sanitized, reputation);\n\n // Stage 6: Rule-of-Two policy enforcement (#3198). Signal only: the\n // consumer enforces — except under `enforce`, where it refuses (#5382).\n const ruleOfTwoViolation = this.runPolicyEnforcement(meta, effectiveTrustTier, context);\n const refusal = this.refuseIfEnforcing(ruleOfTwoViolation, meta, effectiveTrustTier);\n if (refusal !== undefined) return err(refusal);\n\n return ok(\n this.assembleResult({\n sanitized,\n trust,\n // Measured or absent: present only when an allowlist was consulted.\n isAllowlisted: allowlist !== undefined ? trust.isAllowlisted : undefined,\n reputation,\n effectiveTrustTier,\n reputationGate,\n atl,\n ruleOfTwoViolation,\n start,\n })\n );\n }\n\n /**\n * Evaluates the Rule of Two against the call's access posture and surfaces\n * the violation (previously the `policyEnforcement` stage was declared but\n * never read — #3198). Returns `undefined` when the stage is disabled or the\n * rule holds.\n */\n private runPolicyEnforcement(\n meta: SourceMetadata,\n effectiveTrustTier: TrustTier,\n context: { readonly hasWriteAccess: boolean; readonly hasSecretAccess: boolean }\n ): Violation | undefined {\n if (!this.stages.policyEnforcement) return undefined;\n const violation = checkRuleOfTwo({\n inputTrustTier: effectiveTrustTier,\n hasWriteAccess: context.hasWriteAccess,\n hasSecretAccess: context.hasSecretAccess,\n });\n if (violation !== undefined) {\n logger.warn('Firewall surfaced a Rule-of-Two violation', {\n user: meta.username,\n effectiveTrustTier,\n rule: violation.rule,\n });\n }\n return violation;\n }\n\n /**\n * Builds the successful result. Optional fields are spread in only when they\n * were evaluated, so absence keeps meaning \"not measured\" (see the field\n * docs on {@link FirewallResult}).\n */\n private assembleResult(parts: {\n readonly sanitized: SanitizedInput;\n readonly trust: ClassifyResult;\n readonly isAllowlisted: boolean | undefined;\n readonly reputation: ReputationAssessment | undefined;\n readonly effectiveTrustTier: TrustTier;\n readonly reputationGate: ReputationGateDecision | undefined;\n readonly atl: string;\n readonly ruleOfTwoViolation: Violation | undefined;\n readonly start: number;\n }): FirewallResult {\n const { isAllowlisted, reputation, reputationGate, ruleOfTwoViolation } = parts;\n const blocking = ruleOfTwoViolation?.severity === 'block';\n return {\n sanitized: parts.sanitized,\n trust: parts.trust,\n ...(isAllowlisted !== undefined ? { isAllowlisted } : {}),\n ...(reputation !== undefined ? { reputation } : {}),\n effectiveTrustTier: parts.effectiveTrustTier,\n ...(reputationGate !== undefined ? { reputationGate } : {}),\n atl: parts.atl,\n ...(ruleOfTwoViolation !== undefined ? { ruleOfTwoViolation } : {}),\n policyMode: this.policyMode,\n // Reached only when we did NOT refuse, so this is \"audit mode saw\n // something enforce would have stopped\". Under `off` it stays false: the\n // signal is already on `ruleOfTwoViolation`, and reporting a would-be\n // refusal for a mode that has not opted in would overstate the gate.\n wouldRefuse: blocking && this.policyMode === 'audit',\n auditEvents: this.auditTrail.query().map((e) => ({ id: e.id, type: e.type })),\n auditSink: this.auditSink,\n durationMs: Date.now() - parts.start,\n };\n }\n\n /**\n * The fail-closed half of the #5382 gate: under `enforce`, a blocking policy\n * violation refuses the input instead of riding along as a signal on an\n * `ok()` result that a caller checking only `result.ok` walks straight past.\n *\n * This gates the RESPONSE to a violation, never its detection. It cannot\n * manufacture a refusal where the `policyEnforcement` stage never ran, and it\n * cannot refuse a non-blocking (`warn`) violation — so `enforce` is not a\n * kill switch, and an allowlisted maintainer stays served.\n *\n * @returns the refusal, or `undefined` when the input passes or the mode has\n * not opted in.\n */\n private refuseIfEnforcing(\n violation: Violation | undefined,\n meta: SourceMetadata,\n effectiveTrustTier: TrustTier\n ): FirewallError | undefined {\n if (violation?.severity !== 'block' || this.policyMode !== 'enforce') return undefined;\n logger.warn('Firewall REFUSED input under enforce mode', {\n user: meta.username,\n effectiveTrustTier,\n rule: violation.rule,\n });\n return {\n code: 'POLICY_REFUSED',\n message: `Refused by firewall policy: ${violation.rule} — ${violation.message}`,\n stage: 'policy',\n };\n }\n\n /**\n * Validates corroboration for a decided action (#5382).\n *\n * Separate from {@link process} because the two operate at different points\n * in the lifecycle, which is the real shape of the divergence epic #5281\n * found: `process()` is INPUT-shaped — it sanitizes, classifies and labels\n * untrusted content — while corroboration is ACTION-shaped, asking whether a\n * decision the consumer has now reached is backed by sources of sufficient\n * tier. There is no `AgentAction` in scope during `process()`, so the\n * `stages.corroboration` flag could never have been wired there; this is the\n * entry point that makes it readable.\n *\n * It is also the shape #5383 needs: production validates corroboration per\n * action (`issue-triage.ts:391`), so those callers cannot migrate onto the\n * firewall unless it offers a per-action surface.\n *\n * Returns `evaluated: false` when the stage is disabled — never a satisfied\n * verdict for a check that did not run. Under `enforce` an unsatisfied action\n * is refused with `POLICY_REFUSED`; under `audit` the would-be refusal is\n * reported via `wouldRefuse` and the action is allowed through.\n */\n validateAction(action: AgentAction): Result<ActionValidation, FirewallError> {\n if (!this.stages.corroboration) {\n return ok({\n evaluated: false,\n reason: 'corroboration-stage-disabled',\n policyMode: this.policyMode,\n });\n }\n\n const corroboration = validateCorroboration(action);\n\n if (!corroboration.satisfied && this.policyMode === 'enforce') {\n logger.warn('Firewall REFUSED an uncorroborated action under enforce mode', {\n actionType: corroboration.actionType,\n missing: corroboration.missing,\n });\n return err({\n code: 'POLICY_REFUSED',\n message:\n `Refused by firewall policy: ${corroboration.actionType} lacks required ` +\n `corroboration — ${corroboration.missing.join('; ')}`,\n stage: 'corroboration',\n });\n }\n\n return ok({\n evaluated: true,\n satisfied: corroboration.satisfied,\n missing: corroboration.missing,\n corroboratingSources: corroboration.corroboratingSources,\n policyMode: this.policyMode,\n wouldRefuse: !corroboration.satisfied && this.policyMode === 'audit',\n });\n }\n\n /** Returns the internal audit trail for inspection. */\n getAuditTrail(): AuditTrail {\n return this.auditTrail;\n }\n\n // ==========================================================================\n // Pipeline Stages (private)\n // ==========================================================================\n\n private runExtraction(input: unknown): Result<SourceMetadata, FirewallError> {\n try {\n const meta = this.adapter.extractMetadata(input);\n return ok(meta);\n } catch (cause: unknown) {\n const message = cause instanceof Error ? cause.message : String(cause);\n return err({\n code: 'EXTRACTION_FAILED',\n message: `Adapter extraction failed: ${message}`,\n stage: 'extraction',\n });\n }\n }\n\n private runSanitization(\n meta: SourceMetadata,\n allowlist: readonly string[] | undefined\n ): SanitizedInput {\n if (!this.stages.sanitization) {\n return createPassthroughSanitized(meta);\n }\n\n const result = sanitizeInput(\n meta.content,\n mapAuthorAssociation(meta.authorAssociation),\n meta.username,\n {\n allowlistedMaintainers: [...(allowlist ?? [])],\n maxInputLength: this.maxInputLength,\n }\n );\n\n if (this.stages.audit) {\n emitSanitizationEvent(this.auditTrail, {\n source: meta.sourceType,\n wasModified: result.wasModified,\n ...(result.truncated !== undefined ? { truncated: result.truncated } : {}),\n strippedCount: result.strippedElements.length,\n injectionFlagCount: result.injectionFlags.length,\n strippedElements: result.strippedElements\n .slice(0, MAX_STRIPPED_ELEMENTS_PER_EVENT)\n .map((e) => ({ tag: e.tag, reason: e.reason })),\n });\n }\n\n return result;\n }\n\n private runClassification(\n meta: SourceMetadata,\n sanitized: SanitizedInput,\n allowlist: readonly string[] | undefined\n ): ClassifyResult {\n if (!this.stages.trustClassification) {\n return createPassthroughClassification(meta);\n }\n\n return classifyTrust({\n username: meta.username,\n authorAssociation: meta.authorAssociation,\n // #4992: `contentDowngrade: false` withholds the sanitizer's content tier\n // from the classifier; the flags themselves were still measured above.\n ...(this.contentDowngrade && sanitized.contentTierMeasured !== false\n ? { sanitizedInput: sanitized }\n : {}),\n config: {\n allowlistedMaintainers: [...(allowlist ?? [])],\n },\n });\n }\n\n /**\n * Emits the trust audit event for the tier the consumer acts on — after the\n * reputation gate, so the record cannot describe a tier nobody enforced\n * (#4992 review). A demotion is named in `reason`.\n */\n private recordTrustDecision(\n meta: SourceMetadata,\n trust: ClassifyResult,\n effectiveTrustTier: TrustTier,\n gate: ReputationGateDecision | undefined,\n allowlist: readonly string[] | undefined\n ): void {\n if (!this.stages.audit) return;\n const demoted = effectiveTrustTier !== trust.trustTier;\n emitTrustEvent(this.auditTrail, {\n username: meta.username,\n assignedTier: effectiveTrustTier,\n userRole: trust.userRole,\n // Recorded only when an allowlist was consulted (#4992).\n ...(allowlist !== undefined ? { isAllowlisted: trust.isAllowlisted } : {}),\n wasDowngraded: trust.wasDowngraded || demoted,\n reason: describeGate(trust.reason, demoted, gate),\n });\n }\n\n /**\n * Reconcile the classifier tier with reputation, under the rollout mode\n * production honours (#3106 reconciliation, #5381 gating).\n *\n * #3106's reconciliation is demotion-only; Tier-1/allowlist wins; it equals\n * the classifier tier when reputation is absent. #5381 puts it behind\n * `NEXUS_REPUTATION_GATING`, so `audit` is audit-only here too — previously\n * the firewall enforced unconditionally while production suppressed, on\n * identical configuration.\n *\n * Returns no gate at all when the stage is off, so absence in the result\n * means \"not evaluated\" rather than \"evaluated, nothing suppressed\" —\n * `demotionSuppressed` is a required boolean and would otherwise report\n * `false` for a check that never ran.\n */\n private runReputationGate(\n classifierTier: TrustTier,\n reputation: ReputationAssessment | undefined,\n callerSupplied: boolean\n ): { readonly tier: TrustTier; readonly gate?: ReputationGateDecision } {\n // A caller-supplied measurement runs the gate even when the instance's own\n // reputation stage is off (#4992 review): the caller measured, so record it.\n if (!this.stages.reputationAssessment && !callerSupplied) return { tier: classifierTier };\n const gate = gateWithReputation(classifierTier, reputation, this.reputationGatingMode);\n return { tier: gate.enforcedTier, gate };\n }\n\n private runReputation(\n meta: SourceMetadata,\n sanitized: SanitizedInput,\n supplied: { readonly assessment: ReputationAssessment | undefined } | undefined\n ): ReputationAssessment | undefined {\n if (supplied !== undefined) {\n // The caller's measurement replaces the instance's stage for this call;\n // `undefined` means the caller measured nothing, so nothing is emitted.\n if (supplied.assessment !== undefined && this.stages.audit) {\n emitReputationEvent(this.auditTrail, {\n username: meta.username,\n reputationScore: supplied.assessment.reputationScore,\n isSuspicious: supplied.assessment.isSuspicious,\n effectiveTier: supplied.assessment.effectiveTrustTier,\n signalCount: supplied.assessment.suspiciousSignals.length,\n });\n }\n return supplied.assessment;\n }\n if (!this.stages.reputationAssessment) return undefined;\n\n // #3106: only supply what the firewall actually knows from the event —\n // author association + injection flags. Account-age / contribution /\n // recent-comment data is NOT available here (it's fetched at the wiring\n // layer in a later phase), so it is OMITTED, not fabricated. The engine\n // skips the account/activity signals when their data is absent; until the\n // fetch lands, the firewall's reputation reflects injection + authority\n // signals only — honest rather than always-benign.\n const metadata: GitHubUserMetadata = {\n username: meta.username,\n authorAssociation: meta.authorAssociation.toUpperCase(),\n injectionFlags: sanitized.injectionFlags,\n };\n\n const result = this.assessReputationFn(metadata);\n\n if (this.stages.audit) {\n emitReputationEvent(this.auditTrail, {\n username: meta.username,\n reputationScore: result.reputationScore,\n isSuspicious: result.isSuspicious,\n effectiveTier: result.effectiveTrustTier,\n signalCount: result.suspiciousSignals.length,\n });\n }\n\n return result;\n }\n\n private buildATL(\n meta: SourceMetadata,\n effectiveTrustTier: TrustTier,\n sanitized: SanitizedInput,\n reputation?: ReputationAssessment\n ): string {\n const data: ATLData = {\n tier: effectiveTrustTier,\n source: meta.sourceType,\n user: meta.username,\n sanitized: sanitized.wasModified,\n ...(reputation !== undefined ? { rep: reputation.reputationScore / 100 } : {}),\n };\n return generateATL(data);\n }\n}\n","/**\n * nexus-agents/security/firewall - GitHub Adapter\n *\n * ISourceAdapter implementation for GitHub issues, PRs, and comments.\n * Extracts normalized metadata from GitHub API payloads.\n *\n * @module security/firewall/github-adapter\n * (Source: Issue #826 — Reusable Hostile Input Firewall)\n */\n\nimport { z } from 'zod';\n\nimport { mapAuthorAssociation } from '../trust-classifier.js';\nimport type { ISourceAdapter, SourceMetadata } from './firewall-types.js';\n\n// ============================================================================\n// GitHub Input Types\n// ============================================================================\n\n/**\n * Discriminated union of GitHub input types the adapter can process.\n */\nconst GitHubIssueSchema = z.object({\n type: z.literal('issue'),\n username: z.string().min(1),\n authorAssociation: z.string().min(1),\n title: z.string().default(''),\n body: z.string().default(''),\n});\n\nconst GitHubCommentSchema = z.object({\n type: z.literal('comment'),\n username: z.string().min(1),\n authorAssociation: z.string().min(1),\n body: z.string().default(''),\n});\n\nconst GitHubPRSchema = z.object({\n type: z.literal('pull_request'),\n username: z.string().min(1),\n authorAssociation: z.string().min(1),\n title: z.string().default(''),\n body: z.string().default(''),\n});\n\nexport const GitHubInputSchema = z.discriminatedUnion('type', [\n GitHubIssueSchema,\n GitHubCommentSchema,\n GitHubPRSchema,\n]);\nexport type GitHubInput = z.infer<typeof GitHubInputSchema>;\n\n// ============================================================================\n// GitHub Adapter\n// ============================================================================\n\n/** Maps input type to source type string. */\nfunction toSourceType(type: GitHubInput['type']): string {\n const mapping: Record<GitHubInput['type'], string> = {\n issue: 'github-issue',\n comment: 'github-comment',\n pull_request: 'github-pr',\n };\n return mapping[type];\n}\n\n/** Extracts content from a GitHub input. */\nfunction extractContent(input: GitHubInput): string {\n if (input.type === 'comment') return input.body;\n const title = input.title;\n const body = input.body;\n return title && body ? `${title}\\n\\n${body}` : title || body;\n}\n\n/**\n * Creates a GitHub source adapter.\n * Validates input with Zod and maps GitHub API fields to SourceMetadata.\n */\nexport function createGitHubAdapter(): ISourceAdapter {\n return {\n platform: 'github',\n extractMetadata(input: unknown): SourceMetadata {\n const result = GitHubInputSchema.safeParse(input);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => `${i.path.join('.')}: ${i.message}`)\n .join('; ');\n throw new Error(`GitHub input validation failed: ${issues}`);\n }\n const parsed = result.data;\n const role = mapAuthorAssociation(parsed.authorAssociation);\n return {\n username: parsed.username,\n authorAssociation: role,\n content: extractContent(parsed),\n sourceType: toSourceType(parsed.type),\n };\n },\n };\n}\n","/**\n * nexus-agents/dogfooding - Untrusted-input firewall (shared instance)\n *\n * The one `HostileInputFirewall` the live untrusted-input paths route through\n * (#4992): `issue-triage` and `pr-reviewer`. Before this module both called\n * `classifyTrust()` directly, which meant no trust classification on any live\n * path reached the security audit trail, and the maintainer allowlist the\n * classifier reads from `config` was never supplied — `isAllowlisted: false`\n * was recorded on every result without ever being measured.\n *\n * Division of labour, ratified by the #4992 panel:\n *\n * - The firewall is INPUT-shaped: it sanitizes, classifies, evaluates the\n * Rule of Two against the caller's access posture, and records the trust\n * event. Under the default `NEXUS_FIREWALL_POLICY=off` it is signal-only.\n * - The callers keep their own `evaluatePolicy` (Rule of Two per action) and\n * measure reputation themselves with metadata the firewall cannot see\n * (account age, comment history). They pass that measurement per call, and\n * the firewall runs the ONE reputation gate both sides act on — so its\n * Rule-of-Two check, `wouldRefuse` count and trust audit event use the same\n * enforced tier the caller's policy gate uses.\n * - Facts that vary per call — the caller's access posture and, when a source\n * for one exists, the repository's maintainer allowlist — are passed per\n * call via `FirewallProcessOptions`, never held on the shared instance. No\n * allowlist source exists today (no config field, no env var), so the\n * callers pass none and `isAllowlisted` stays absent on their results.\n *\n * @module dogfooding/untrusted-input-firewall\n */\n\nimport type { Result } from '../core/index.js';\nimport { ok, err, createLogger } from '../core/index.js';\nimport type { IAuditLogger } from '../audit/audit-types.js';\nimport { HostileInputFirewall } from '../security/firewall/firewall-pipeline.js';\nimport type { FirewallResult } from '../security/firewall/firewall-pipeline.js';\nimport type { FirewallProcessOptions } from '../security/firewall/firewall-types.js';\nimport { createGitHubAdapter } from '../security/firewall/github-adapter.js';\nimport type { GitHubInput } from '../security/firewall/github-adapter.js';\n\nconst logger = createLogger({ component: 'UntrustedInputFirewall' });\n\nlet singleton: HostileInputFirewall | undefined;\n/** The process's durable audit logger, when the bootstrap has one (#4992 review). */\nlet configuredAuditLogger: IAuditLogger | undefined;\n\n/**\n * Supplies the process-wide durable audit logger to the shared firewall.\n *\n * The MCP server creates exactly one `AuditLogger` at startup\n * (`initializeAuditLogger`, gated on `security.audit.enabled`) and threads it\n * by DI; there is no global accessor. `initV2PipelineSubsystems` calls this\n * with that logger so trust events on the live paths reach the hash-chained\n * log instead of an in-memory buffer the next call clears. Without it — the\n * CLI review path, or audit disabled — the firewall reports `auditSink: 'none'`\n * and claims no emission.\n *\n * Replaces the cached instance so the sink cannot be missed by an instance\n * built before bootstrap reached this point.\n */\nexport function configureUntrustedInputFirewall(deps: { auditLogger?: IAuditLogger }): void {\n configuredAuditLogger = deps.auditLogger;\n singleton = undefined;\n}\n\n/**\n * The process-wide firewall, constructed on first use.\n *\n * `contentDowngrade: false` is what keeps the default mode pass-through: the\n * firewall's own classifier is content-aware (injection-bearing content from a\n * Tier-2 author classifies as Tier 4), while the production paths classify by\n * role and route content signals through reputation gating, which has its own\n * rollout knob (`NEXUS_REPUTATION_GATING`). The injection flags are still\n * measured and recorded by the sanitization stage; only the tier downgrade is\n * withheld so the recorded `trustTier` is unchanged under `off`.\n *\n * The reputation stage stays off: the callers assess reputation themselves\n * with metadata the firewall never sees. `policyMode` is resolved from\n * `NEXUS_FIREWALL_POLICY` once, at construction.\n */\nexport function getUntrustedInputFirewall(): HostileInputFirewall {\n singleton ??= new HostileInputFirewall({\n adapter: createGitHubAdapter(),\n contentDowngrade: false,\n ...(configuredAuditLogger !== undefined ? { auditLogger: configuredAuditLogger } : {}),\n });\n return singleton;\n}\n\n// @export-no-consumer-yet — see #4992. Test-only seam: drops or replaces the\n// cached instance so a test can re-read the env or observe the audit trail.\nexport function _setUntrustedInputFirewallForTests(\n firewall: HostileInputFirewall | undefined\n): void {\n singleton = firewall;\n}\n\n/**\n * Runs one untrusted GitHub payload through the shared firewall.\n *\n * Maps the firewall's typed error into the `Result<_, Error>` shape the\n * callers already return, failing closed on every code: an unparseable payload\n * (`EXTRACTION_FAILED`) is as much a reason not to proceed as a policy\n * refusal (`POLICY_REFUSED`, only reachable under `enforce`). Under `audit` a\n * would-be refusal is logged and the result is returned unchanged — that log\n * line is the telemetry an operator sizes the flip to `enforce` on.\n */\nexport function runUntrustedInputFirewall(\n input: GitHubInput,\n options: FirewallProcessOptions\n): Result<FirewallResult, Error> {\n const result = getUntrustedInputFirewall().process(input, options);\n if (!result.ok) {\n const { code, stage, message } = result.error;\n logger.warn('Untrusted-input firewall rejected the input', {\n user: input.username,\n sourceType: input.type,\n code,\n stage,\n });\n return err(new Error(`Untrusted-input firewall ${code} at stage ${stage}: ${message}`));\n }\n if (result.value.wouldRefuse) {\n logger.warn('Untrusted-input firewall would refuse under enforce (audit mode)', {\n user: input.username,\n sourceType: input.type,\n trustTier: result.value.effectiveTrustTier,\n rule: result.value.ruleOfTwoViolation?.rule,\n });\n }\n return ok(result.value);\n}\n","/**\n * nexus-agents/scm - URL parsers\n *\n * Pure parsers for GitHub PR and issue URLs. Lifted from\n * `dogfooding/github-client.ts` as part of the #2553 consolidation —\n * URL parsing has no transport-layer dependency, so it belongs in the\n * canonical SCM module alongside the rest of the GitHub surface.\n *\n * @module scm/url-parsers\n */\n\nimport type { Result } from '../core/index.js';\nimport { ok, err } from '../core/index.js';\n\n/**\n * Parses a PR URL into owner, repo, and number. Accepts:\n *\n * - `https://github.com/owner/repo/pull/123`\n * - `https://www.github.com/owner/repo/pull/123`\n * - `owner/repo#123`\n * - `owner/repo/pull/123`\n */\nexport function parsePRUrl(url: string): Result<\n {\n owner: string;\n repo: string;\n prNumber: number;\n },\n Error\n> {\n const httpPattern = /github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/(\\d+)/;\n const shortPattern = /^([^/]+)\\/([^/#]+)(?:#|\\/pull\\/)(\\d+)$/;\n\n const match = httpPattern.exec(url) ?? shortPattern.exec(url);\n\n if (match === null) {\n return err(new Error(`Invalid PR URL format: ${url}`));\n }\n\n const owner = match[1];\n const repo = match[2];\n const numberStr = match[3];\n\n if (owner === undefined || repo === undefined || numberStr === undefined) {\n return err(new Error(`Invalid PR URL format: ${url}`));\n }\n\n const prNumber = parseInt(numberStr, 10);\n\n if (isNaN(prNumber)) {\n return err(new Error(`Invalid PR URL format: ${url}`));\n }\n\n return ok({ owner, repo, prNumber });\n}\n\n/**\n * Parses an issue URL into owner, repo, and number. Accepts:\n *\n * - `https://github.com/owner/repo/issues/123`\n * - `owner/repo#123`\n */\nexport function parseIssueUrl(url: string): Result<\n {\n owner: string;\n repo: string;\n issueNumber: number;\n },\n Error\n> {\n const httpPattern = /github\\.com\\/([^/]+)\\/([^/]+)\\/issues\\/(\\d+)/;\n const shortPattern = /^([^/]+)\\/([^/#]+)#(\\d+)$/;\n\n const match = httpPattern.exec(url) ?? shortPattern.exec(url);\n\n if (match === null) {\n return err(new Error(`Invalid issue URL format: ${url}`));\n }\n\n const owner = match[1];\n const repo = match[2];\n const numberStr = match[3];\n\n if (owner === undefined || repo === undefined || numberStr === undefined) {\n return err(new Error(`Invalid issue URL format: ${url}`));\n }\n\n const issueNumber = parseInt(numberStr, 10);\n\n if (isNaN(issueNumber)) {\n return err(new Error(`Invalid issue URL format: ${url}`));\n }\n\n return ok({ owner, repo, issueNumber });\n}\n","/**\n * nexus-agents/scm - GitHub Provider Trait Implementations\n *\n * Implements IScmReviewer and IScmUserInfo trait interfaces for GitHub.\n * Uses `gh api` for REST API access to get detailed data.\n *\n * @module scm/github-provider-traits\n * (Source: Issue #1136 — Centralized SCM Provider Module)\n */\n\nimport type { Result } from '../core/index.js';\nimport { ok, err, createLogger } from '../core/index.js';\nimport type {\n IScmReviewer,\n IScmUserInfo,\n ScmPullRequestDetail,\n ScmIssueDetail,\n ScmCommentDetail,\n ScmReviewDecision,\n ScmUserMetadata,\n ScmFileChange,\n} from './types.js';\nimport { ScmError } from './types.js';\nimport { GitHubProvider } from './github-provider.js';\nimport { CLI_SUBPROCESS_TIMEOUTS } from '../config/timeouts.js';\n\nconst logger = createLogger({ component: 'GitHubProviderTraits' });\n\n// ============================================================================\n// gh API JSON types (internal)\n// ============================================================================\n\ninterface GhApiPrJson {\n number: number;\n title: string;\n body: string | null;\n html_url: string;\n user: { login: string };\n author_association: string;\n base: { ref: string };\n head: { ref: string; sha: string };\n draft: boolean;\n labels: Array<{ name: string }>;\n additions: number;\n deletions: number;\n}\n\ninterface GhApiFileJson {\n filename: string;\n status: string;\n additions: number;\n deletions: number;\n patch?: string;\n previous_filename?: string;\n}\n\ninterface GhApiIssueJson {\n number: number;\n title: string;\n body: string | null;\n user: { login: string };\n author_association: string;\n state: string;\n html_url: string;\n labels: Array<{ name: string }>;\n created_at: string;\n}\n\ninterface GhApiCommentJson {\n id: number;\n body: string;\n user: { login: string };\n author_association: string;\n created_at: string;\n}\n\ninterface GhApiUserJson {\n login: string;\n name: string | null;\n company: string | null;\n followers: number;\n following: number;\n public_repos: number;\n created_at: string;\n}\n\n// ============================================================================\n// gh API executor\n// ============================================================================\n\n/**\n * Resolves a GitHub token from env vars or `gh auth token`.\n * Caches the result to avoid repeated subprocess calls.\n */\nlet cachedGhToken: string | undefined | null = null;\nlet ghTokenPromise: Promise<string | undefined> | undefined;\n\n/** Reset the cached token (for testing). */\nexport function resetGhTokenCache(): void {\n cachedGhToken = null;\n ghTokenPromise = undefined;\n}\n\nasync function resolveGhToken(): Promise<string | undefined> {\n if (cachedGhToken !== null) return cachedGhToken;\n\n ghTokenPromise ??= resolveGhTokenImpl().finally(() => {\n ghTokenPromise = undefined;\n });\n return ghTokenPromise;\n}\n\nasync function resolveGhTokenImpl(): Promise<string | undefined> {\n const envToken = process.env['GITHUB_TOKEN'] ?? process.env['GH_TOKEN'];\n if (envToken !== undefined && envToken.length > 0) {\n cachedGhToken = envToken;\n return envToken;\n }\n\n try {\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const exec = promisify(execFile);\n const { stdout } = await exec('gh', ['auth', 'token'], { timeout: 5_000 });\n const token = stdout.trim();\n cachedGhToken = token.length > 0 ? token : undefined;\n } catch {\n cachedGhToken = undefined;\n }\n return cachedGhToken;\n}\n\nasync function execGhApi(endpoint: string, method?: string): Promise<Result<string, ScmError>> {\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const exec = promisify(execFile);\n\n const args = ['api', endpoint];\n if (method !== undefined) args.push('--method', method);\n\n // Inject token into subprocess environment to prevent keyring access failures\n const token = await resolveGhToken();\n const env = token !== undefined ? { ...process.env, GH_TOKEN: token } : undefined;\n\n try {\n const { stdout } = await exec('gh', args, {\n maxBuffer: 10 * 1024 * 1024,\n timeout: CLI_SUBPROCESS_TIMEOUTS.ghCommandMs,\n ...(env !== undefined ? { env } : {}),\n });\n return ok(stdout.trim());\n } catch (error) {\n const execError = error as { message: string; stderr?: string };\n return err(\n new ScmError(`gh api failed: ${execError.message}`, 'github', undefined, {\n endpoint,\n stderr: execError.stderr,\n })\n );\n }\n}\n\n// ============================================================================\n// Mappers\n// ============================================================================\n\nfunction mapFileChange(raw: GhApiFileJson): ScmFileChange {\n const statusMap: Record<string, ScmFileChange['status']> = {\n added: 'added',\n removed: 'removed',\n modified: 'modified',\n renamed: 'renamed',\n copied: 'copied',\n changed: 'changed',\n unchanged: 'unchanged',\n };\n const status = statusMap[raw.status] ?? 'unknown';\n return {\n filename: raw.filename,\n status,\n ...(status === 'unknown' ? { rawStatus: raw.status } : {}),\n additions: raw.additions,\n deletions: raw.deletions,\n ...(raw.patch !== undefined ? { patch: raw.patch } : {}),\n ...(raw.previous_filename !== undefined ? { previousFilename: raw.previous_filename } : {}),\n };\n}\n\n// ============================================================================\n// GitHubReviewer — implements IScmReviewer\n// ============================================================================\n\n/**\n * GitHub-specific reviewer that adds PR detail and review capabilities\n * to a GitHubProvider. Implements IScmReviewer trait.\n *\n * @example\n * ```typescript\n * const provider = createGitHubProvider('owner/repo');\n * const reviewer = new GitHubReviewer(provider);\n * const detail = await reviewer.getPullRequestDetail(42);\n * ```\n */\nexport class GitHubReviewer implements IScmReviewer {\n constructor(private readonly provider: GitHubProvider) {}\n\n async getPullRequestDetail(prNumber: number): Promise<Result<ScmPullRequestDetail, ScmError>> {\n const repo = this.provider.repo;\n logger.debug('Getting PR detail', { repo, prNumber });\n\n const prResult = await execGhApi(`repos/${repo}/pulls/${String(prNumber)}`);\n if (!prResult.ok) return prResult;\n\n const filesResult = await execGhApi(`repos/${repo}/pulls/${String(prNumber)}/files`);\n if (!filesResult.ok) return filesResult;\n\n try {\n const pr = JSON.parse(prResult.value) as GhApiPrJson;\n const files = JSON.parse(filesResult.value) as GhApiFileJson[];\n\n return ok({\n number: pr.number,\n title: pr.title,\n body: pr.body ?? '',\n author: pr.user.login,\n base: pr.base.ref,\n head: pr.head.ref,\n url: pr.html_url,\n draft: pr.draft,\n authorAssociation: pr.author_association,\n labels: pr.labels.map((l) => l.name),\n files: files.map(mapFileChange),\n additions: pr.additions,\n deletions: pr.deletions,\n headSha: pr.head.sha,\n });\n } catch {\n return err(new ScmError('Failed to parse PR detail JSON', 'github'));\n }\n }\n\n async createReview(\n prNumber: number,\n body: string,\n decision: ScmReviewDecision\n ): Promise<Result<void, ScmError>> {\n const repo = this.provider.repo;\n const eventMap: Record<ScmReviewDecision, string> = {\n approve: 'APPROVE',\n request_changes: 'REQUEST_CHANGES',\n comment: 'COMMENT',\n };\n\n logger.info('Creating review', { repo, prNumber, decision });\n\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const exec = promisify(execFile);\n\n try {\n await exec(\n 'gh',\n [\n 'api',\n `repos/${repo}/pulls/${String(prNumber)}/reviews`,\n '--method',\n 'POST',\n '-f',\n `body=${body}`,\n '-f',\n `event=${eventMap[decision]}`,\n ],\n { maxBuffer: 10 * 1024 * 1024, timeout: CLI_SUBPROCESS_TIMEOUTS.ghCommandMs }\n );\n return ok(undefined);\n } catch (error) {\n const execError = error as { message: string };\n return err(new ScmError(`Failed to create review: ${execError.message}`, 'github'));\n }\n }\n\n async getIssueDetail(issueNumber: number): Promise<Result<ScmIssueDetail, ScmError>> {\n const repo = this.provider.repo;\n logger.debug('Getting issue detail', { repo, issueNumber });\n\n const result = await execGhApi(`repos/${repo}/issues/${String(issueNumber)}`);\n if (!result.ok) return result;\n\n try {\n const raw = JSON.parse(result.value) as GhApiIssueJson;\n return ok({\n number: raw.number,\n title: raw.title,\n body: raw.body ?? '',\n labels: raw.labels.map((l) => l.name),\n author: raw.user.login,\n createdAt: raw.created_at,\n authorAssociation: raw.author_association,\n state: raw.state,\n url: raw.html_url,\n });\n } catch {\n return err(new ScmError('Failed to parse issue detail JSON', 'github'));\n }\n }\n\n async listCommentDetails(\n issueNumber: number\n ): Promise<Result<readonly ScmCommentDetail[], ScmError>> {\n const repo = this.provider.repo;\n logger.debug('Listing comment details', { repo, issueNumber });\n\n const result = await execGhApi(`repos/${repo}/issues/${String(issueNumber)}/comments`);\n if (!result.ok) return result;\n\n try {\n const comments = JSON.parse(result.value) as GhApiCommentJson[];\n return ok(\n comments.map((c) => ({\n id: c.id,\n body: c.body,\n author: c.user.login,\n createdAt: c.created_at,\n authorAssociation: c.author_association,\n }))\n );\n } catch {\n return err(new ScmError('Failed to parse comment details JSON', 'github'));\n }\n }\n}\n\n// ============================================================================\n// GitHubUserInfo — implements IScmUserInfo\n// ============================================================================\n\n/**\n * GitHub-specific user info provider. Implements IScmUserInfo trait.\n */\nexport class GitHubUserInfo implements IScmUserInfo {\n async fetchUserMetadata(username: string): Promise<Result<ScmUserMetadata, ScmError>> {\n logger.debug('Fetching user metadata', { username });\n\n const result = await execGhApi(`users/${username}`);\n if (!result.ok) return result;\n\n try {\n const raw = JSON.parse(result.value) as GhApiUserJson;\n return ok({\n login: raw.login,\n name: raw.name,\n company: raw.company,\n followers: raw.followers,\n following: raw.following,\n publicRepos: raw.public_repos,\n createdAt: raw.created_at,\n });\n } catch {\n return err(new ScmError('Failed to parse user metadata JSON', 'github'));\n }\n }\n}\n\n// ============================================================================\n// Factory — compose provider with traits\n// ============================================================================\n\n/**\n * Creates a full-capability GitHub provider with all traits.\n *\n * Returns an object that implements IScmProvider & IScmReviewer & IScmUserInfo.\n * Consumers can narrow the type to only the traits they need.\n *\n * @example\n * ```typescript\n * const provider = createFullGitHubProvider('owner/repo');\n * // Use as ReviewCapableProvider\n * const detail = await provider.getPullRequestDetail(42);\n * // Use as IScmUserInfo\n * const user = await provider.fetchUserMetadata('octocat');\n * ```\n */\nexport function createFullGitHubProvider(\n repo: string\n): GitHubProvider & IScmReviewer & IScmUserInfo {\n const base = new GitHubProvider(repo);\n const reviewer = new GitHubReviewer(base);\n const userInfo = new GitHubUserInfo();\n\n // Compose all trait methods onto the provider\n return Object.assign(base, {\n getPullRequestDetail: reviewer.getPullRequestDetail.bind(reviewer),\n createReview: reviewer.createReview.bind(reviewer),\n getIssueDetail: reviewer.getIssueDetail.bind(reviewer),\n listCommentDetails: reviewer.listCommentDetails.bind(reviewer),\n fetchUserMetadata: userInfo.fetchUserMetadata.bind(userInfo),\n });\n}\n","/**\n * nexus-agents/dogfooding - Issue Triage Types\n *\n * Type definitions for automated GitHub issue triage using\n * the security pipeline (trust classification, corroboration,\n * reputation model).\n *\n * @module dogfooding/issue-triage-types\n * (Source: Issue #828 — Wire remaining security modules)\n */\n\nimport { z } from 'zod';\nimport type { ReputationAssessment } from '../security/reputation-model.js';\n\n// ============================================================================\n// Issue Metadata\n// ============================================================================\n\n/**\n * GitHub issue metadata from the API.\n */\nexport interface IssueMetadata {\n /** Issue number */\n readonly number: number;\n /** Issue title */\n readonly title: string;\n /** Issue body/description */\n readonly body: string;\n /** Author username */\n readonly author: string;\n /** GitHub API author_association (e.g., 'OWNER', 'COLLABORATOR', 'NONE') */\n readonly authorAssociation: string;\n /** Repository owner */\n readonly owner: string;\n /** Repository name */\n readonly repo: string;\n /** Issue URL */\n readonly url: string;\n /** Issue state ('open' | 'closed') */\n readonly state: string;\n /** Issue labels */\n readonly labels: readonly string[];\n /** Creation timestamp (ISO 8601) */\n readonly createdAt: string;\n}\n\n/**\n * GitHub issue comment from the API.\n */\nexport interface IssueComment {\n /** Comment ID */\n readonly id: number;\n /** Comment body */\n readonly body: string;\n /** Author username */\n readonly author: string;\n /** GitHub API author_association */\n readonly authorAssociation: string;\n /** Creation timestamp (ISO 8601) */\n readonly createdAt: string;\n}\n\n// ============================================================================\n// Issue Classification\n// ============================================================================\n\n/**\n * Issue category determined by keyword-based classification.\n */\nexport type IssueCategory =\n 'bug' | 'feature' | 'question' | 'documentation' | 'security' | 'performance';\n\n/**\n * Display names for issue categories.\n */\nexport const CATEGORY_DISPLAY_NAMES: Record<IssueCategory, string> = {\n bug: 'Bug Report',\n feature: 'Feature Request',\n question: 'Question',\n documentation: 'Documentation',\n security: 'Security',\n performance: 'Performance',\n};\n\n/**\n * Emoji for issue categories (GitHub markdown).\n */\nexport const CATEGORY_EMOJI: Record<IssueCategory, string> = {\n bug: ':bug:',\n feature: ':sparkles:',\n question: ':question:',\n documentation: ':books:',\n security: ':lock:',\n performance: ':zap:',\n};\n\n// ============================================================================\n// Triage Configuration\n// ============================================================================\n\n/**\n * Configuration for issue triage.\n */\nexport interface IssueTriageConfig {\n /** Whether to run in dry-run mode (no GitHub mutations). Default: true */\n readonly dryRun: boolean;\n /** GitHub token for API access */\n readonly githubToken?: string | undefined;\n /** Maximum comments to fetch per issue */\n readonly maxComments: number;\n /** Whether to use reputation model for trust assessment */\n readonly enableReputation: boolean;\n}\n\n/**\n * Default issue triage configuration.\n * Read-only by default — proposes actions without executing them.\n */\nexport const DEFAULT_ISSUE_TRIAGE_CONFIG: IssueTriageConfig = {\n dryRun: true,\n maxComments: 50,\n enableReputation: true,\n};\n\n/**\n * Zod schema for issue triage configuration.\n */\nexport const IssueTriageConfigSchema = z.object({\n dryRun: z.boolean().default(true),\n githubToken: z.string().optional(),\n maxComments: z.number().int().min(1).max(100).default(50),\n enableReputation: z.boolean().default(true),\n});\n\n// ============================================================================\n// Proposed Actions\n// ============================================================================\n\n/**\n * A proposed action from the triage pipeline.\n * All actions are validated through the security pipeline before output.\n */\nexport interface ProposedAction {\n /** Action type (matches AgentActionType from security/action-schema) */\n readonly type: string;\n /** Human-readable description of the proposed action */\n readonly description: string;\n /** Whether this action was approved by the policy gate */\n readonly policyApproved: boolean;\n /** Whether corroboration requirements were satisfied */\n readonly corroborated: boolean;\n /** Details specific to the action type */\n readonly details: Record<string, unknown>;\n}\n\n// ============================================================================\n// Trust Assessment\n// ============================================================================\n\n/**\n * Trust assessment summary for the triage result.\n */\nexport interface TrustAssessment {\n /** Author's trust tier (1-4) */\n readonly trustTier: string;\n /** Author's GitHub role */\n readonly userRole: string;\n /**\n * Whether the author is on the maintainer allowlist — present only when an\n * allowlist was consulted (#4992). Absent means \"no allowlist was consulted\",\n * which is the state on every live path today; it was previously recorded\n * as `false` without being measured.\n */\n readonly isAllowlisted?: boolean;\n /**\n * Whether a durable `AuditLogger` was configured for this process (#4992\n * review). `configured` — the trust event was handed to it; delivery to the\n * hash chain is subject to the logger's own severity filter (trust events\n * are `info`), queue and fail-loud flush, and is not confirmed per call.\n * `none` — no audit logger (audit disabled, or the CLI path), so the event\n * exists only in the firewall's in-memory trail.\n */\n readonly auditSink: 'configured' | 'none';\n /** Reputation score (0-100) if reputation model is enabled */\n readonly reputationScore?: number | undefined;\n /** Coverage of reputation dimensions; `unmeasured` is distinct from zero activity. */\n readonly coverage?: ReputationAssessment['coverage'] | undefined;\n /** Suspicious signals detected */\n readonly suspiciousSignals: readonly string[];\n /** Whether the author is flagged as suspicious */\n readonly isSuspicious: boolean;\n /**\n * Tier the policy gate ACTUALLY enforced (#3122). Equals `trustTier` under\n * `audit`/`off`; equals `reputationReconciledTier` under `enforce`. This is\n * the tier `proposedActions[].policyApproved` was decided against.\n */\n readonly enforcedTrustTier?: string | undefined;\n /**\n * Tier reputation reconciliation computed — what `enforce` mode WOULD gate on\n * (#3122). May exceed `trustTier` (more restrictive) when reputation demotes.\n * Under `audit`/`off` this is reported for telemetry but NOT enforced — read\n * `enforcedTrustTier` for the tier actually in effect.\n */\n readonly reputationReconciledTier?: string | undefined;\n /** Reputation-gating rollout mode applied: `off` | `audit` | `enforce` (#3122). */\n readonly gatingMode?: string | undefined;\n}\n\n// ============================================================================\n// Triage Result\n// ============================================================================\n\n/**\n * Complete triage result for a GitHub issue.\n */\nexport interface IssueTriageResult {\n /** Issue number */\n readonly issueNumber: number;\n /** Repository (owner/repo) */\n readonly repository: string;\n /** Proposed actions (validated through security pipeline) */\n readonly proposedActions: readonly ProposedAction[];\n /** Trust assessment of the issue author */\n readonly trustAssessment: TrustAssessment;\n /** Detected issue category */\n readonly category: IssueCategory;\n /** Category confidence (0-1) */\n readonly categoryConfidence: number;\n /** Total execution time in ms */\n readonly totalDurationMs: number;\n /** Timestamp of triage (ISO 8601) */\n readonly timestamp: string;\n}\n","/**\n * nexus-agents/dogfooding - Issue Triage Helpers\n *\n * Pure helper functions for issue classification, label extraction,\n * and result formatting. No side effects or API calls.\n *\n * @module dogfooding/issue-triage-helpers\n * (Source: Issue #828 — Wire remaining security modules)\n */\n\nimport type { CorroborationResult } from '../security/corroboration-validator.js';\nimport type { AgentAction } from '../security/action-schema.js';\nimport type { ScmCommentDetail } from '../scm/types.js';\nimport type {\n IssueCategory,\n IssueComment,\n IssueTriageResult,\n ProposedAction,\n} from './issue-triage-types.js';\nimport { CATEGORY_DISPLAY_NAMES, CATEGORY_EMOJI } from './issue-triage-types.js';\n\n// ============================================================================\n// Issue Classification\n// ============================================================================\n\n/**\n * Keyword sets for each issue category.\n * Lower-cased for case-insensitive matching.\n */\nconst CATEGORY_KEYWORDS: Record<IssueCategory, readonly string[]> = {\n bug: ['bug', 'error', 'crash', 'broken', 'fix', 'fail', 'issue', 'wrong', 'unexpected'],\n feature: ['feature', 'request', 'enhancement', 'proposal', 'add', 'support', 'implement'],\n question: ['question', 'how to', 'help', 'confused', 'explain', 'documentation'],\n documentation: ['docs', 'documentation', 'readme', 'typo', 'example', 'guide'],\n security: ['security', 'vulnerability', 'cve', 'exploit', 'injection', 'xss', 'csrf'],\n performance: ['performance', 'slow', 'memory', 'leak', 'optimize', 'latency', 'timeout'],\n};\n\n/**\n * Classifies an issue by matching keywords in the title and body.\n * Returns the category with the highest keyword match count.\n *\n * @param title - Issue title\n * @param body - Issue body\n * @returns Tuple of [category, confidence]\n */\nexport function categorizeIssue(title: string, body: string): [IssueCategory, number] {\n const text = `${title} ${body}`.toLowerCase();\n const scores: Record<IssueCategory, number> = {\n bug: 0,\n feature: 0,\n question: 0,\n documentation: 0,\n security: 0,\n performance: 0,\n };\n\n let totalMatches = 0;\n\n for (const [category, keywords] of Object.entries(CATEGORY_KEYWORDS)) {\n for (const keyword of keywords) {\n if (text.includes(keyword)) {\n scores[category as IssueCategory]++;\n totalMatches++;\n }\n }\n }\n\n // Find category with highest score\n let bestCategory: IssueCategory = 'bug';\n let bestScore = 0;\n for (const [category, score] of Object.entries(scores)) {\n if (score > bestScore) {\n bestScore = score;\n bestCategory = category as IssueCategory;\n }\n }\n\n // Confidence: ratio of best score to total matches, minimum 0.1\n const confidence = totalMatches > 0 ? Math.min(bestScore / totalMatches, 1) : 0.1;\n\n return [bestCategory, Math.round(confidence * 100) / 100];\n}\n\n// ============================================================================\n// Label Extraction\n// ============================================================================\n\n/**\n * Common label patterns that can be extracted from issue text.\n */\nconst LABEL_HINTS: ReadonlyMap<string, string> = new Map([\n ['bug', 'bug'],\n ['feature request', 'enhancement'],\n ['enhancement', 'enhancement'],\n ['breaking change', 'breaking-change'],\n ['documentation', 'documentation'],\n ['help wanted', 'help wanted'],\n ['good first issue', 'good first issue'],\n ['security', 'security'],\n ['performance', 'performance'],\n ['regression', 'regression'],\n]);\n\n/**\n * Extracts suggested labels from issue title and body text.\n *\n * @param title - Issue title\n * @param body - Issue body\n * @returns Array of suggested label strings (max 5)\n */\nexport function extractLabelsFromBody(title: string, body: string): string[] {\n const text = `${title} ${body}`.toLowerCase();\n const labels: string[] = [];\n\n for (const [pattern, label] of LABEL_HINTS) {\n if (text.includes(pattern) && !labels.includes(label)) {\n labels.push(label);\n }\n }\n\n return labels.slice(0, 5);\n}\n\n/** Maps SCM comment details to the triage representation. */\nexport function mapIssueComments(comments: readonly ScmCommentDetail[]): IssueComment[] {\n return comments.map((comment) => ({\n id: comment.id,\n body: comment.body,\n author: comment.author,\n authorAssociation: comment.authorAssociation,\n createdAt: comment.createdAt,\n }));\n}\n\n/** Creates a human-readable description for a typed action. */\nexport function describeAction(action: AgentAction): string {\n switch (action.type) {\n case 'ClassifyIssue':\n return `Classified as ${action.category} (${String(Math.round(action.confidence * 100))}% confidence)`;\n case 'ProposeLabels':\n return `Suggest labels: ${action.labels.join(', ')}`;\n case 'SummarizeIssue':\n return action.summary.slice(0, 100);\n default:\n return `${action.type} action`;\n }\n}\n\n/** Builds details object for a proposed action. */\nexport function buildActionDetails(\n action: AgentAction,\n policy: { allowed: boolean; violations: readonly { rule: string; message: string }[] },\n corrob: CorroborationResult\n): Record<string, unknown> {\n return {\n policyViolations: policy.violations.map((violation) => violation.rule),\n missingCorroboration: corrob.missing,\n ...(action.type === 'ClassifyIssue' && { category: action.category }),\n ...(action.type === 'ProposeLabels' && { labels: action.labels }),\n };\n}\n\n// ============================================================================\n// Result Formatting\n// ============================================================================\n\n/**\n * Formats a triage result as a GitHub markdown comment.\n *\n * @param result - The complete triage result\n * @returns Formatted markdown string\n */\nexport function formatTriageComment(result: IssueTriageResult): string {\n const emoji = CATEGORY_EMOJI[result.category];\n const categoryName = CATEGORY_DISPLAY_NAMES[result.category];\n const lines: string[] = [];\n\n lines.push(`## ${emoji} Issue Triage: ${categoryName}`);\n lines.push('');\n lines.push(\n `**Category:** ${categoryName} (${String(Math.round(result.categoryConfidence * 100))}% confidence)`\n );\n lines.push(\n `**Trust Tier:** ${result.trustAssessment.trustTier} (${result.trustAssessment.userRole})`\n );\n\n if (result.trustAssessment.reputationScore !== undefined) {\n lines.push(`**Reputation Score:** ${String(result.trustAssessment.reputationScore)}/100`);\n }\n\n if (result.trustAssessment.isSuspicious) {\n lines.push('');\n lines.push(':warning: **Suspicious signals detected:**');\n for (const signal of result.trustAssessment.suspiciousSignals) {\n lines.push(`- ${signal}`);\n }\n }\n\n if (result.proposedActions.length > 0) {\n lines.push('');\n lines.push('### Proposed Actions');\n lines.push('');\n for (const action of result.proposedActions) {\n const status = formatActionStatus(action);\n lines.push(`- ${status} **${action.type}**: ${action.description}`);\n }\n }\n\n lines.push('');\n lines.push(`---`);\n lines.push(`_Triage completed in ${String(result.totalDurationMs)}ms_`);\n\n return lines.join('\\n');\n}\n\n/**\n * Formats the policy/corroboration status of an action.\n */\nfunction formatActionStatus(action: ProposedAction): string {\n if (action.policyApproved && action.corroborated) return ':white_check_mark:';\n if (action.policyApproved && !action.corroborated) return ':yellow_circle:';\n return ':no_entry:';\n}\n","/**\n * nexus-agents/dogfooding - Issue Triage Processor\n *\n * Deterministic GitHub issue triage using the full security pipeline:\n * - Input sanitization (input-sanitizer.ts)\n * - Trust classification (trust-classifier.ts)\n * - Reputation assessment (reputation-model.ts) [NEW — #828]\n * - Typed agent actions (action-schema.ts)\n * - Policy gate validation (policy-gate.ts)\n * - Corroboration validation (corroboration-validator.ts) [NEW — #828]\n *\n * Read-only by default (dryRun: true). Proposes actions but never\n * auto-posts to GitHub without explicit opt-in.\n *\n * @module dogfooding/issue-triage\n * (Source: Issue #828 — Wire remaining security modules)\n */\n\nimport type { Result } from '../core/index.js';\nimport { ok, err, createLogger, getTimeProvider } from '../core/index.js';\nimport { sanitizeInput } from '../security/input-sanitizer.js';\nimport { hasToken } from '../scm/token-resolver.js';\nimport type { ClassifyResult } from '../security/trust-classifier.js';\nimport type { FirewallResult } from '../security/firewall/firewall-pipeline.js';\nimport { runUntrustedInputFirewall } from './untrusted-input-firewall.js';\nimport type { TrustTier } from '../security/trust-types.js';\nimport { evaluatePolicy } from '../security/policy-gate.js';\nimport type { ActionContext } from '../security/policy-gate.js';\nimport { validateCorroboration } from '../security/corroboration-validator.js';\nimport { assessReputation, ReputationCache } from '../security/reputation-model.js';\nimport type {\n ReputationAssessment,\n GitHubUserMetadata,\n ReputationGateDecision,\n} from '../security/reputation-model.js';\nimport type { AgentAction, SourceCitation } from '../security/action-schema.js';\nimport { parseIssueUrl } from '../scm/url-parsers.js';\nimport { createFullGitHubProvider } from '../scm/github-provider-traits.js';\nimport type { ScmUserMetadata } from '../scm/types.js';\nimport {\n buildActionDetails,\n categorizeIssue,\n describeAction,\n extractLabelsFromBody,\n mapIssueComments,\n} from './issue-triage-helpers.js';\nimport type {\n IssueMetadata,\n IssueComment,\n IssueTriageConfig,\n IssueTriageResult,\n ProposedAction,\n TrustAssessment,\n} from './issue-triage-types.js';\nimport { DEFAULT_ISSUE_TRIAGE_CONFIG } from './issue-triage-types.js';\n\nconst logger = createLogger({ component: 'IssueTriage' });\n\n/**\n * Applies the two safety actions the untrusted-input rules mandate (#4667).\n *\n * They are mutually exclusive by construction: a hostile tier refuses, a\n * suspicious-but-not-hostile one escalates. Emitting both would ask a human to\n * approve something the system has already declined.\n */\nfunction applySafetyActions(\n actions: readonly AgentAction[],\n gateDecision: ReputationGateDecision,\n reputation: ReputationAssessment | undefined,\n issue: IssueMetadata\n): AgentAction[] {\n const withRefusal = appendRefusalIfHostile(actions, gateDecision, issue);\n return appendApprovalIfBorderline(withRefusal, gateDecision, reputation, issue);\n}\n\n/**\n * Appends a `RequestHumanApproval` for suspicious-but-not-hostile input (#4667).\n *\n * Triage was binary: tier 4 refuses, everything else proceeds. That left no way\n * to express the common real case — reputation noticed something (a new\n * account, rapid comments, a weak signal) without it amounting to hostility.\n * Those proposals are not safe to auto-apply and not fair to refuse, so they\n * escalate.\n *\n * Deliberately mutually exclusive with the refusal: a hostile tier already\n * emits `RefuseAction`, and stacking \"refused\" with \"please approve\" would ask\n * a human to approve something the system just declined.\n */\nfunction appendApprovalIfBorderline(\n actions: readonly AgentAction[],\n gateDecision: ReputationGateDecision,\n reputation: ReputationAssessment | undefined,\n issue: IssueMetadata\n): AgentAction[] {\n if (gateDecision.enforcedTier === '4') return [...actions];\n // Tier 1 is the allowlist escape hatch — asking a maintainer to approve a\n // maintainer's own issue is noise, and it would undo the \"allowlist wins\"\n // guarantee that tier exists to provide.\n if (gateDecision.enforcedTier === '1') return [...actions];\n if (reputation?.isSuspicious !== true) return [...actions];\n\n // `no_prior_contributions` counts the author's comments ON THIS ISSUE\n // (`countAuthorComments(author, comments)`), so a freshly filed issue always\n // trips it — it is true for essentially every new issue and therefore carries\n // no information. Escalating on it alone would escalate on everything, which\n // is how an approval gate becomes noise and then gets ignored. Require at\n // least one signal that actually distinguishes this author.\n const distinguishing = reputation.suspiciousSignals.filter(\n (sig) => sig !== 'no_prior_contributions'\n );\n if (distinguishing.length === 0) return [...actions];\n\n const signals = distinguishing.join(', ');\n return [\n ...actions,\n {\n type: 'RequestHumanApproval',\n reason:\n `Issue #${String(issue.number)} carries suspicious reputation signals but did not ` +\n `reach the hostile tier. Proposed actions need a human decision.`,\n context:\n `Author ${issue.author} at enforced tier ${gateDecision.enforcedTier} ` +\n `(${gateDecision.mode} mode). Signals: ${signals.length > 0 ? signals : '(none recorded)'}.`,\n },\n ];\n}\n\n/**\n * Appends an explicit `RefuseAction` when the enforced tier is hostile (#4667).\n *\n * The generated actions are kept rather than replaced: they are the audit trail\n * of what was declined, and every one of them is already blocked by the policy\n * gate at tier 4. What was missing is a positive statement that the input was\n * refused and where to escalate it.\n */\nfunction appendRefusalIfHostile(\n actions: readonly AgentAction[],\n gateDecision: ReputationGateDecision,\n issue: IssueMetadata\n): AgentAction[] {\n if (gateDecision.enforcedTier !== '4') return [...actions];\n return [\n ...actions,\n {\n type: 'RefuseAction',\n reason:\n `Issue #${String(issue.number)} was classified at hostile trust tier 4 ` +\n `(${gateDecision.mode} mode). Automated triage actions are withheld pending human review.`,\n escalateTo: 'security',\n },\n ];\n}\n\n/**\n * GitHub issue triage processor.\n *\n * Wires all 8 security modules into a read-only triage pipeline:\n * 1. Fetch issue + comments from GitHub API\n * 2. Sanitize untrusted content (input-sanitizer)\n * 3. Classify author trust (trust-classifier)\n * 4. Assess author reputation (reputation-model) [#828]\n * 5. Generate typed actions (action-schema)\n * 6. Validate through policy gate (policy-gate)\n * 7. Validate corroboration (corroboration-validator) [#828]\n * 8. Return proposed actions\n */\nexport class IssueTriage {\n private readonly config: IssueTriageConfig;\n private readonly reputationCache: ReputationCache;\n\n constructor(config?: Partial<IssueTriageConfig>) {\n this.config = { ...DEFAULT_ISSUE_TRIAGE_CONFIG, ...config };\n this.reputationCache = new ReputationCache();\n }\n\n /**\n * Triages a GitHub issue through the full security pipeline.\n */\n async triageIssue(issueUrl: string): Promise<Result<IssueTriageResult, Error>> {\n const startTime = getTimeProvider().now();\n\n logger.info('Starting issue triage', { issueUrl });\n\n const parseResult = parseIssueUrl(issueUrl);\n if (!parseResult.ok) return parseResult;\n\n const { owner, repo, issueNumber } = parseResult.value;\n\n const fetchResult = await this.fetchIssueData(owner, repo, issueNumber);\n if (!fetchResult.ok) return fetchResult;\n\n const { issue: issueResult, existingLabels } = fetchResult.value;\n\n // Sanitize untrusted content (Issue #828 — input-sanitizer wiring)\n const safeTitle = this.sanitizeContent(issueResult.title, issueResult.author);\n const safeBody = this.sanitizeContent(issueResult.body, issueResult.author);\n\n // Reputation is measured here, with data the firewall cannot see (account\n // age, comment history), and handed to the firewall per call.\n const reputation = this.assessAuthorReputation(fetchResult.value);\n\n // #4992: classify through the shared HostileInputFirewall so the trust\n // decision reaches the security audit trail. Signal-only under the default\n // NEXUS_FIREWALL_POLICY=off; `validateActions` below stays the Rule-of-Two\n // enforcement point. The firewall runs the ONE reputation gate (#3122:\n // off/audit/enforce; audit reports a would-be demotion without enforcing\n // it; the allowlist Tier 1 remains the escape hatch), so its Rule-of-Two\n // signal and this path's policy gate act on the same enforced tier.\n const firewall = this.classifyAuthor(issueResult, reputation);\n if (!firewall.ok) return firewall;\n const trustResult = firewall.value.trust;\n const gateDecision = firewall.value.reputationGate;\n if (gateDecision === undefined) {\n // Structurally unreachable — the option is always passed — but a missing\n // gate must not be papered over with the classifier tier.\n return err(new Error('Untrusted-input firewall returned no reputation gate decision'));\n }\n if (gateDecision.demotionSuppressed) {\n logger.warn('Reputation demotion suppressed by gating mode (would block under enforce)', {\n issueNumber,\n author: issueResult.author,\n mode: gateDecision.mode,\n classifierTier: trustResult.trustTier,\n reconciledTier: gateDecision.reconciledTier,\n });\n }\n\n // Generate and validate actions\n const actions = this.generateActions(safeTitle, safeBody, issueResult, trustResult);\n // #4667: a hostile enforced tier must produce an explicit refusal. Before\n // this, tier 4 merely meant every generated action failed the policy gate —\n // the caller saw fewer approved actions and no statement that anything was\n // refused, so the fail-closed escalation the rules mandate had no producer.\n // RefuseAction is tier-4 \"always allowed\" (trust-classifier.ts:170), so it\n // survives the gate that blocks the rest.\n const withEscalation = applySafetyActions(actions, gateDecision, reputation, issueResult);\n const validatedActions = this.validateActions(withEscalation, gateDecision, existingLabels);\n\n const result = this.buildResult({\n issue: issueResult,\n actions: validatedActions,\n trustResult,\n isAllowlisted: firewall.value.isAllowlisted,\n auditSink: firewall.value.auditSink,\n reputation,\n gateDecision,\n safeContent: { title: safeTitle, body: safeBody },\n startTime,\n });\n\n logger.info('Issue triage completed', {\n issueNumber,\n category: result.category,\n actionCount: result.proposedActions.length,\n durationMs: result.totalDurationMs,\n });\n\n return ok(result);\n }\n\n /**\n * Sanitizes untrusted issue content before processing.\n * (Source: Issue #828 — input-sanitizer wiring)\n */\n private sanitizeContent(text: string, author: string): string {\n if (text.length === 0) return text;\n const result = sanitizeInput(text, 'unknown', author);\n if (result.wasModified) {\n logger.warn('Issue content sanitized', {\n author,\n strippedCount: result.strippedElements.length,\n injectionFlags: result.injectionFlags,\n });\n }\n return result.content;\n }\n\n /**\n * Classifies the issue author's trust tier through the shared firewall\n * (#4992; originally Issue #828 — trust-classifier wiring).\n *\n * The access posture is passed per call so the firewall's Rule-of-Two check\n * sees the same facts `validateActions` enforces on. No maintainer allowlist\n * is passed: there is no source for one today (no config field, no env var),\n * so none is consulted and `isAllowlisted` stays absent on the result rather\n * than being recorded as `false`.\n */\n private classifyAuthor(\n issue: IssueMetadata,\n reputation: ReputationAssessment | undefined\n ): Result<FirewallResult, Error> {\n return runUntrustedInputFirewall(\n {\n type: 'issue',\n username: issue.author,\n authorAssociation: issue.authorAssociation,\n title: issue.title,\n body: issue.body,\n },\n { context: this.accessContext(), reputation: { assessment: reputation } }\n );\n }\n\n /**\n * The access posture this triage runs under — the two Rule-of-Two conjuncts\n * that are about the agent rather than the input. Shared by the firewall\n * (#4992) and `validateActions` so the two cannot disagree.\n */\n private accessContext(): { hasWriteAccess: boolean; hasSecretAccess: boolean } {\n return {\n hasWriteAccess: !this.config.dryRun,\n // #4681: read the token from where it ACTUALLY comes from. The previous\n // form (`this.config.githubToken !== undefined`) was written to make this\n // conjunct \"real\", but no production caller ever sets `githubToken` — the\n // SCM provider resolves the live credential from GITHUB_TOKEN/GH_TOKEN.\n // So the conjunct was permanently false and Rule of Two could never trip,\n // which is precisely the constant it was introduced to remove.\n // `hasToken()` is the canonical resolver-backed check.\n hasSecretAccess: this.config.githubToken !== undefined || hasToken('github'),\n };\n }\n\n /**\n * Assesses the issue author's reputation using the reputation model.\n * This is one of the two NEW security module wirings completing #828.\n * (Source: Issue #828 — reputation-model wiring)\n */\n private assessAuthorReputation(data: {\n issue: IssueMetadata;\n comments: readonly IssueComment[];\n commentsAvailable: boolean;\n accountAgeDays?: number;\n }): ReputationAssessment | undefined {\n if (!this.config.enableReputation) return undefined;\n const { issue, comments, commentsAvailable, accountAgeDays } = data;\n\n // #4681: scan BOTH the title and the body. Scanning only the body left a\n // real bypass: an injection payload is plain text, so content-sanitization\n // does not strip it from the title, and its flags were discarded here — the\n // identical payload was refused at Tier 4 in the body but raised no signal\n // at all in the title, then reached the emitted SummarizeIssue verbatim.\n const bodyScan = sanitizeInput(issue.body, 'unknown', issue.author);\n const titleScan = sanitizeInput(issue.title, 'unknown', issue.author);\n const injectionFlags = [...new Set([...titleScan.injectionFlags, ...bodyScan.injectionFlags])];\n\n // #3121: accountAgeDays is the author's REAL account age (fetched in\n // fetchIssueData) or undefined when the lookup failed. When undefined it is\n // OMITTED so the engine skips the new_account signal (#3106) — never\n // fabricated. priorContributions/recentCommentCount use real comment data.\n const metadata: GitHubUserMetadata = {\n username: issue.author,\n ...(accountAgeDays !== undefined ? { accountAgeDays } : {}),\n ...(commentsAvailable\n ? {\n priorContributions: countAuthorComments(issue.author, comments),\n recentCommentCount: countRecentComments(issue.author, comments),\n recentCommentWindowMinutes: 10,\n }\n : {}),\n authorAssociation: issue.authorAssociation,\n injectionFlags,\n };\n\n return assessReputation(metadata, commentsAvailable ? this.reputationCache : undefined);\n }\n\n /**\n * Generates typed agent actions based on issue analysis.\n */\n private generateActions(\n safeTitle: string,\n safeBody: string,\n issue: IssueMetadata,\n trustResult: ClassifyResult\n ): AgentAction[] {\n const actions: AgentAction[] = [];\n const source = this.createIssueSource(issue, trustResult.trustTier);\n\n // Always generate ClassifyIssue action\n const [category, confidence] = categorizeIssue(safeTitle, safeBody);\n actions.push({\n type: 'ClassifyIssue',\n category,\n confidence,\n sources: [source],\n });\n\n // Generate ProposeLabels if we found label hints\n const labels = extractLabelsFromBody(safeTitle, safeBody);\n if (labels.length > 0) {\n actions.push({\n type: 'ProposeLabels',\n labels,\n reason: `Keyword-based label extraction from issue #${String(issue.number)}`,\n sources: [source],\n });\n }\n\n // Generate SummarizeIssue for semi-trusted+ authors\n if (trustResult.trustTier === '1' || trustResult.trustTier === '2') {\n const summary = `Issue #${String(issue.number)} \"${safeTitle}\" by ${issue.author} (${trustResult.userRole})`;\n actions.push({\n type: 'SummarizeIssue',\n summary: summary.length >= 10 ? summary : `${summary} — awaiting further analysis`,\n sources: [source],\n });\n }\n\n return actions;\n }\n\n /**\n * Validates all actions through policy gate and corroboration validator.\n * This completes the #828 wiring by integrating corroboration-validator.\n * (Source: Issue #828 — policy-gate + corroboration-validator wiring)\n */\n private validateActions(\n actions: readonly AgentAction[],\n gateDecision: ReputationGateDecision,\n existingLabels: ReadonlySet<string> | undefined\n ): ProposedAction[] {\n // #3119 + #3122: reputation GATES via the rollout mode. `enforce` uses the\n // reconciled (possibly demoted) tier; `audit`/`off` enforce the classifier\n // tier (the suppressed demotion is logged upstream). Demotion-only;\n // Tier-1/allowlist always wins.\n const context: ActionContext = {\n inputTrustTier: gateDecision.enforcedTier,\n ...this.accessContext(),\n ...(existingLabels !== undefined ? { existingLabels } : {}),\n };\n\n return actions.map((action) => {\n const policyDecision = evaluatePolicy(action, context);\n const corrobResult = validateCorroboration(action);\n\n return {\n type: action.type,\n description: describeAction(action),\n policyApproved: policyDecision.allowed,\n corroborated: corrobResult.satisfied,\n details: buildActionDetails(action, policyDecision, corrobResult),\n };\n });\n }\n\n /**\n * Builds the final triage result.\n */\n private buildResult(opts: {\n issue: IssueMetadata;\n actions: readonly ProposedAction[];\n trustResult: ClassifyResult;\n /** Present only when the firewall consulted an allowlist (#4992). */\n isAllowlisted: boolean | undefined;\n auditSink: FirewallResult['auditSink'];\n reputation: ReputationAssessment | undefined;\n gateDecision: ReputationGateDecision;\n safeContent: { title: string; body: string };\n startTime: number;\n }): IssueTriageResult {\n const {\n issue,\n actions,\n trustResult,\n isAllowlisted,\n auditSink,\n reputation,\n gateDecision,\n safeContent,\n startTime,\n } = opts;\n const [category, confidence] = categorizeIssue(safeContent.title, safeContent.body);\n\n // Tier 1 actors (owner/maintainer) cannot be suspicious — reconcile\n // the trust-classifier result with the reputation-model result.\n const isTier1 = trustResult.trustTier === '1';\n\n const trustAssessment: TrustAssessment = {\n trustTier: trustResult.trustTier,\n userRole: trustResult.userRole,\n // Measured or absent (#4992): never the classifier's default `false`.\n ...(isAllowlisted !== undefined ? { isAllowlisted } : {}),\n auditSink,\n reputationScore: reputation?.reputationScore,\n coverage: reputation?.coverage,\n suspiciousSignals: isTier1 ? [] : (reputation?.suspiciousSignals ?? []),\n isSuspicious: isTier1 ? false : (reputation?.isSuspicious ?? false),\n // #3122: surface both the enforced tier (what the gate used) and the\n // reconciled tier (what reputation computed) so telemetry can't mistake\n // a would-be demotion for an enforced one.\n enforcedTrustTier: gateDecision.enforcedTier,\n reputationReconciledTier: gateDecision.reconciledTier,\n gatingMode: gateDecision.mode,\n };\n\n return {\n issueNumber: issue.number,\n repository: `${issue.owner}/${issue.repo}`,\n proposedActions: actions,\n trustAssessment,\n category,\n categoryConfidence: confidence,\n totalDurationMs: getTimeProvider().now() - startTime,\n timestamp: getTimeProvider().nowIso(),\n };\n }\n\n /**\n * Creates a repo file source citation for the triage.\n */\n private createIssueSource(issue: IssueMetadata, trustTier: TrustTier): SourceCitation {\n return {\n type: 'issueBody',\n issueNumber: issue.number,\n author: issue.author,\n authorTrustTier: trustTier,\n };\n }\n\n /**\n * Fetches issue data from the SCM provider and maps to dogfooding types.\n */\n private async fetchIssueData(\n owner: string,\n repo: string,\n issueNumber: number\n ): Promise<\n Result<\n {\n issue: IssueMetadata;\n comments: IssueComment[];\n commentsAvailable: boolean;\n accountAgeDays?: number;\n existingLabels?: ReadonlySet<string>;\n },\n Error\n >\n > {\n const provider = createFullGitHubProvider(`${owner}/${repo}`);\n\n const detailResult = await provider.getIssueDetail(issueNumber);\n if (!detailResult.ok) return err(detailResult.error);\n\n const detail = detailResult.value;\n const issue: IssueMetadata = { ...detail, owner, repo };\n\n const commentsResult = await provider.listCommentDetails(issueNumber);\n if (!commentsResult.ok) {\n logger.warn('Failed to fetch issue comments; activity reputation is unmeasured', {\n issueNumber,\n error: commentsResult.error.message,\n });\n }\n const comments = commentsResult.ok ? mapIssueComments(commentsResult.value) : [];\n const commentsAvailable = commentsResult.ok;\n\n const labelsResult = await provider.listRepositoryLabels();\n if (!labelsResult.ok) {\n logger.warn('Failed to fetch repository labels; label validity is unmeasured', {\n issueNumber,\n error: labelsResult.error.message,\n });\n }\n\n // #3121: fetch the author's REAL account age (their account creation date,\n // not the issue date). Best-effort — on failure or an unparseable date we\n // omit it, so the reputation engine SKIPS the new_account signal (#3106)\n // rather than fabricating a value. Reputation must not block on this.\n const accountAgeDays = await this.fetchAccountAgeDays(provider, detail.author);\n\n return ok({\n issue,\n comments,\n commentsAvailable,\n ...(accountAgeDays !== undefined ? { accountAgeDays } : {}),\n ...(labelsResult.ok ? { existingLabels: new Set(labelsResult.value) } : {}),\n });\n }\n\n /**\n * Best-effort lookup of a GitHub user's account age in days. Returns\n * `undefined` if the user-metadata fetch fails or the creation date can't be\n * parsed (#3121) — callers must treat absence as \"unknown\", not \"benign\".\n */\n private async fetchAccountAgeDays(\n provider: { fetchUserMetadata: (u: string) => Promise<Result<ScmUserMetadata, Error>> },\n username: string\n ): Promise<number | undefined> {\n try {\n const result = await provider.fetchUserMetadata(username);\n if (!result.ok) return undefined;\n const createdMs = Date.parse(result.value.createdAt);\n if (!Number.isFinite(createdMs)) return undefined;\n return Math.floor((getTimeProvider().now() - createdMs) / 86_400_000);\n } catch {\n // Truly best-effort: even an unexpected rejection (token resolution,\n // transport) must not break triage — fall back to \"unknown\" age.\n return undefined;\n }\n }\n}\n\n/** Counts how many comments the author has made on the issue. */\nfunction countAuthorComments(author: string, comments: readonly IssueComment[]): number {\n return comments.filter((comment) => comment.author === author).length;\n}\n\n/** Counts recent comments from the author (within 10 minutes). */\nfunction countRecentComments(author: string, comments: readonly IssueComment[]): number {\n const tenMinutesAgo = Date.now() - 10 * 60 * 1000;\n return comments.filter(\n (comment) => comment.author === author && new Date(comment.createdAt).getTime() > tenMinutesAgo\n ).length;\n}\n\n/**\n * Creates an IssueTriage instance.\n */\nexport function createIssueTriage(config?: Partial<IssueTriageConfig>): IssueTriage {\n return new IssueTriage(config);\n}\n"],"mappings":"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","names":["logger","z","z","z","z","z","z","logger","z","z","logger","logger","logger","logger","z","z","logger"]}
|
|
1
|
+
{"version":3,"sources":["../src/security/firewall/firewall-policy-mode.ts","../src/security/audit-bridge.ts","../src/security/trust-classifier.ts","../src/security/firewall/firewall-types.ts","../src/security/firewall/agent-trust-labels.ts","../src/security/firewall/firewall-passthrough.ts","../src/security/firewall/firewall-trust-reason.ts","../src/security/policy-gate.ts","../src/security/action-schema.ts","../src/security/corroboration-validator.ts","../src/security/firewall/firewall-pipeline.ts","../src/security/firewall/github-adapter.ts","../src/dogfooding/untrusted-input-firewall.ts","../src/scm/url-parsers.ts","../src/scm/github-provider-traits.ts","../src/dogfooding/issue-triage-types.ts","../src/dogfooding/issue-triage-helpers.ts","../src/dogfooding/issue-triage.ts"],"sourcesContent":["/**\n * Rollout gate for `HostileInputFirewall` behaviour changes (#5382, epic #5281).\n *\n * `HostileInputFirewall` is a PUBLISHED API: re-exported through\n * `src/exports/security.ts`, carried in `api-surface.txt`, and pinned by an\n * export-contract test. Epic #5281 found it has fallen BEHIND the hand-composed\n * production path rather than being dead scaffolding, and its remaining children\n * change what `process()` decides — #5380 raises one policy check to seven,\n * #5381 makes reputation gating mode-aware.\n *\n * A supermajority panel ratified this gate FIRST (6 approve / 1 reject; the lone\n * rejection argued the gate binds even more firmly than proposed) so those\n * changes have somewhere to land that is not a silent behaviour change shipped\n * in a patch release to consumers this repo cannot see.\n *\n * The invariant that makes it a gate: **`off` is the default, and under `off`\n * behaviour is byte-identical to pre-#5382.** A gate whose default changes\n * behaviour has not gated anything.\n *\n * Deliberately NOT a new mechanism. This is the third flag of exactly this shape\n * — `NEXUS_ACCESS_POLICY_MODE` (ClawGuard, #1977) and `NEXUS_REPUTATION_GATING`\n * (#3122) are the other two — so it delegates to the shared `resolveEnvMode`\n * helper (#3130) and coerces identically. `access-constraint-deriver/types.ts`\n * notes these env-var names are a stability contract; adding a fourth resolver\n * with subtly different coercion is the sprawl that helper exists to prevent.\n *\n * @module security/firewall/firewall-policy-mode\n */\n\nimport { z } from 'zod';\nimport { resolveEnvMode } from '../env-mode.js';\nimport type { ILogger } from '../../core/index.js';\n\n/**\n * Rollout state for firewall policy behaviour.\n *\n * - `off` — pre-#5382 behaviour exactly. The default.\n * - `audit` — compute the stricter outcome and REPORT it, but enforce the old\n * one. This is the mode that makes a rollout measurable: it answers \"what\n * would change?\" without changing it.\n * - `enforce` — apply the stricter outcome.\n */\nexport const FirewallPolicyModeSchema = z.enum(['off', 'audit', 'enforce']);\nexport type FirewallPolicyMode = z.infer<typeof FirewallPolicyModeSchema>;\n\n/**\n * Default is `off`, and that is the compatibility promise, not a placeholder.\n *\n * Note this differs from `DEFAULT_REPUTATION_GATING_MODE`, which is `enforce`\n * (#4667) — that flag governs an INTERNAL path this repo owns end to end, and\n * was flipped only after measurement over the real triage path. This one\n * governs a published surface with unknown external callers, so it starts off\n * and stays off until the same kind of measurement justifies a flip. Any future\n * flip is a MAJOR version change, not a patch.\n */\nexport const DEFAULT_FIREWALL_POLICY_MODE: FirewallPolicyMode = 'off';\n\n/** Env var carrying the mode. Named to match its two sibling flags. */\nexport const FIREWALL_POLICY_ENV_VAR = 'NEXUS_FIREWALL_POLICY';\n\n/**\n * Resolve the firewall policy mode from the environment.\n *\n * Never throws, because a security layer must not fail-closed at startup on an\n * operator typo (#3130). The two non-happy paths resolve DIFFERENTLY, though:\n * unset or empty resolves silently to `off` (absence is the normal state), while\n * an explicit-but-invalid value resolves to `audit` and emits one `warn`.\n *\n * @param env Environment to read (injectable for tests).\n * @param logger Injectable for tests; defaults to the shared module logger.\n */\nexport function resolveFirewallPolicyMode(\n env: NodeJS.ProcessEnv = process.env,\n logger?: ILogger\n): FirewallPolicyMode {\n return resolveEnvMode(\n env[FIREWALL_POLICY_ENV_VAR],\n FirewallPolicyModeSchema,\n DEFAULT_FIREWALL_POLICY_MODE,\n FIREWALL_POLICY_ENV_VAR,\n {\n ...(logger !== undefined ? { logger } : {}),\n // An explicit-but-invalid value lands on `audit`, NOT on the unset\n // default `off`. Unset means \"the operator has not opted in\" and `off` is\n // right for it; a typo means \"the operator opted in and mistyped how\",\n // and answering that with the most permissive mode hands them a firewall\n // that refuses nothing while they believe it is on. `audit` reports\n // without refusing, so it cannot break an external caller any more than\n // `off` can, and it emits the `wouldRefuse` telemetry the rollout is\n // supposed to be collecting — which `off` does not.\n invalidFallback: 'audit',\n }\n );\n}\n","/**\n * Security → durable audit bridge (#3291, epic #3288 item 3).\n *\n * The security `AuditTrail` (audit-trail.ts) is in-memory-only, so every\n * trust/policy/reputation/sanitization decision is lost on process exit. The\n * durable `AuditLogger` (audit/) persists with a tamper-evident hash chain but\n * lacks these rich security event types. This bridge maps each security\n * `AuditEvent` (the discriminated union) into a durable `AuditEventInput` and\n * forwards it to an `IAuditLogger`, so security decisions become durable and\n * hash-verifiable — satisfying CLAUDE.md's \"immutable audit\" mandate.\n *\n * Per the #3291 confirmation vote (fold-in design): security events are folded\n * into the durable schema with `category`/`action` distinguishing them\n * (queryable by `action: 'security.*'`), rather than standing up a parallel\n * SecurityAuditLogger.\n *\n * @module security/audit-bridge\n */\n\nimport { createLogger, getErrorMessage } from '../core/index.js';\nimport { createAuditTrail } from './audit-trail.js';\nimport type {\n AuditEvent as SecurityAuditEvent,\n AuditTrail,\n DurableAuditSink,\n} from './audit-trail.js';\nimport type {\n AuditEventInput,\n AuditActor,\n AuditOutcome,\n AuditSeverity,\n IAuditLogger,\n} from '../audit/audit-types.js';\n\nconst logger = createLogger({ component: 'SecurityAuditBridge' });\n\nfunction systemActor(id: string, name?: string): AuditActor {\n return name !== undefined ? { type: 'system', id, name } : { type: 'system', id };\n}\n\nfunction userActor(username: string): AuditActor {\n return { type: 'user', id: username };\n}\n\ntype TrustEvent = Extract<SecurityAuditEvent, { type: 'trust_classification' }>;\ntype PolicyEvent = Extract<SecurityAuditEvent, { type: 'policy_gate' }>;\ntype CorroborationEvt = Extract<SecurityAuditEvent, { type: 'corroboration' }>;\ntype ReputationEvt = Extract<SecurityAuditEvent, { type: 'reputation' }>;\ntype SanitizationEvt = Extract<SecurityAuditEvent, { type: 'sanitization' }>;\ntype GraphEvt = Extract<SecurityAuditEvent, { type: 'graph_execution' }>;\ntype ClawGuardEvt = Extract<SecurityAuditEvent, { type: 'clawguard_violation' }>;\n\nfunction mapTrust(e: TrustEvent): AuditEventInput {\n const severity: AuditSeverity = e.wasDowngraded ? 'warning' : 'info';\n return {\n category: 'authorization',\n severity,\n outcome: 'success',\n action: 'security.trust_classification',\n actor: userActor(e.username),\n description: e.reason,\n metadata: {\n assignedTier: e.assignedTier,\n userRole: e.userRole,\n // Absent when no allowlist was consulted (#4992) — never a default false.\n ...(e.isAllowlisted !== undefined ? { isAllowlisted: e.isAllowlisted } : {}),\n wasDowngraded: e.wasDowngraded,\n component: e.component,\n },\n };\n}\n\n/**\n * Optional pipeline-policy metadata fields, absent on the security path. Extracted\n * so {@link mapPolicyGate} stays under the complexity cap. `actionType`/`mode`/\n * `ruleIds`/`stageType` are the #3710 round-trip fields; `recordKind`/\n * `violationCount` are #3727 (the summary/violation discriminator + per-evaluation\n * count the durable would-block rate needs).\n */\nfunction pipelinePolicyMetadata(e: PolicyEvent): Record<string, unknown> {\n return {\n ...(e.actionType !== undefined ? { actionType: e.actionType } : {}),\n ...(e.mode !== undefined ? { mode: e.mode } : {}),\n ...(e.ruleIds !== undefined ? { ruleIds: e.ruleIds } : {}),\n ...(e.stageType !== undefined ? { stageType: e.stageType } : {}),\n ...(e.recordKind !== undefined ? { recordKind: e.recordKind } : {}),\n ...(e.violationCount !== undefined ? { violationCount: e.violationCount } : {}),\n };\n}\n\nfunction mapPolicyGate(e: PolicyEvent): AuditEventInput {\n const outcome: AuditOutcome = e.allowed ? 'success' : 'denied';\n // #3710/#3727: the pipeline-policy path carries mode/ruleIds/stageType +\n // recordKind/violationCount and no actionType; these MUST round-trip into the\n // durable metadata (see pipelinePolicyMetadata). The security path leaves them\n // undefined and keeps its actionType-keyed shape.\n return {\n category: 'authorization',\n severity: e.allowed ? 'info' : 'warning',\n outcome,\n action: 'security.policy_gate',\n actor: systemActor(e.component),\n policyName: 'security.policy_gate',\n policyDecision: e.allowed ? 'allow' : 'deny',\n ...(e.violationRules.length > 0 ? { violationType: e.violationRules.join(',') } : {}),\n metadata: {\n requiresApproval: e.requiresApproval,\n inputTrustTier: e.inputTrustTier,\n violationRules: e.violationRules,\n ...pipelinePolicyMetadata(e),\n },\n };\n}\n\nfunction mapCorroboration(e: CorroborationEvt): AuditEventInput {\n return {\n category: 'security',\n severity: e.satisfied ? 'info' : 'warning',\n outcome: e.satisfied ? 'success' : 'denied',\n action: 'security.corroboration',\n actor: systemActor(e.component),\n metadata: {\n actionType: e.actionType,\n sourceCount: e.sourceCount,\n missingRequirements: e.missingRequirements,\n },\n };\n}\n\nfunction mapReputation(e: ReputationEvt): AuditEventInput {\n return {\n category: 'security',\n severity: e.isSuspicious ? 'warning' : 'info',\n outcome: 'success',\n action: 'security.reputation',\n actor: userActor(e.username),\n metadata: {\n reputationScore: e.reputationScore,\n isSuspicious: e.isSuspicious,\n effectiveTier: e.effectiveTier,\n signalCount: e.signalCount,\n component: e.component,\n },\n };\n}\n\nfunction mapSanitization(e: SanitizationEvt): AuditEventInput {\n return {\n category: 'security',\n severity: e.injectionFlagCount > 0 ? 'warning' : 'info',\n outcome: 'success',\n action: 'security.sanitization',\n actor: systemActor(e.component, e.source),\n metadata: {\n source: e.source,\n wasModified: e.wasModified,\n ...(e.truncated !== undefined ? { truncated: e.truncated } : {}),\n strippedCount: e.strippedCount,\n injectionFlagCount: e.injectionFlagCount,\n strippedElements: e.strippedElements,\n },\n };\n}\n\nfunction mapGraphExecution(e: GraphEvt): AuditEventInput {\n return {\n category: 'system',\n severity: 'info',\n outcome: 'success',\n action: 'security.graph_execution',\n actor: systemActor(e.component),\n description: e.detail,\n metadata: {\n graphEvent: e.graphEvent,\n ...(e.nodeId !== undefined ? { nodeId: e.nodeId } : {}),\n stepNumber: e.stepNumber,\n },\n };\n}\n\n/**\n * Map a ClawGuard AUDIT-mode violation (#4097) to a durable event. `outcome` is\n * `success` because audit mode ALLOWED the call (it is log-and-allow, not a\n * denial); `severity` is `warning` to flag the policy violation. Queryable by\n * `action: 'security.clawguard_violation'`.\n */\nfunction mapClawGuard(e: ClawGuardEvt): AuditEventInput {\n return {\n category: 'authorization',\n severity: 'warning',\n outcome: 'success',\n action: 'security.clawguard_violation',\n actor: systemActor(e.component),\n metadata: {\n toolName: e.toolName,\n warning: e.warning,\n policySource: e.policySource,\n mode: e.mode,\n requestId: e.requestId,\n },\n };\n}\n\n/**\n * Map a security `AuditEvent` (discriminated union) to a durable\n * `AuditEventInput`. Pure — no side effects. The durable logger assigns\n * id/timestamp/hash, so those are omitted here.\n */\nexport function securityAuditEventToInput(event: SecurityAuditEvent): AuditEventInput {\n switch (event.type) {\n case 'trust_classification':\n return mapTrust(event);\n case 'policy_gate':\n return mapPolicyGate(event);\n case 'corroboration':\n return mapCorroboration(event);\n case 'reputation':\n return mapReputation(event);\n case 'sanitization':\n return mapSanitization(event);\n case 'graph_execution':\n return mapGraphExecution(event);\n case 'clawguard_violation':\n return mapClawGuard(event);\n }\n}\n\n/**\n * Build a {@link SecurityAuditSink} that maps security events into the durable\n * schema and writes them through `auditLogger`. Errors are swallowed and logged\n * — durable mirroring must never break the security pipeline.\n */\nexport function createDurableAuditSink(auditLogger: IAuditLogger): DurableAuditSink {\n return (event: SecurityAuditEvent) => {\n try {\n auditLogger.log(securityAuditEventToInput(event));\n } catch (error) {\n logger.warn('Failed to mirror security event to durable audit log', {\n type: event.type,\n error: getErrorMessage(error),\n });\n }\n };\n}\n\n/**\n * Wrap an optional `auditLogger` into a durable {@link AuditTrail} (#4097) — the\n * single representation of \"mirror security events to the shared hash chain when\n * a logger is threaded.\" Returns undefined when none is present, so the no-logger\n * path establishes NO trail and stays byte-identical (mirrors the dev-pipeline\n * `buildPolicyAuditTrail` guard). Consumers wrap execution in `withAuditTrail`\n * ONLY when this returns a trail.\n */\nexport function createDurableAuditTrail(auditLogger?: IAuditLogger): AuditTrail | undefined {\n if (auditLogger === undefined) return undefined;\n return createAuditTrail(createDurableAuditSink(auditLogger));\n}\n","/**\n * nexus-agents/security - Trust Classifier\n *\n * Classifies GitHub users and content into trust tiers based on\n * repository relationship, allowlist membership, and content analysis.\n * Works with the input sanitizer to determine how agent decisions\n * should weight each input source.\n *\n * @module security/trust-classifier\n * (Source: Issue #818, #819 — Phase 1: Input Sanitization)\n */\n\nimport type { TrustTier, GitHubUserRole, SanitizedInput, SanitizerConfig } from './trust-types.js';\nimport { ROLE_DEFAULT_TRUST, TRUST_TIER_NUMERIC } from './trust-types.js';\n\n// ============================================================================\n// GitHub Author Association Mapping\n// ============================================================================\n\n/**\n * Maps GitHub API author_association values to our GitHubUserRole enum.\n * See: https://docs.github.com/en/graphql/reference/enums#commentauthorassociation\n */\nexport function mapAuthorAssociation(association: string): GitHubUserRole {\n switch (association.toUpperCase()) {\n case 'OWNER':\n return 'owner';\n case 'MEMBER':\n return 'member';\n case 'COLLABORATOR':\n return 'collaborator';\n case 'CONTRIBUTOR':\n return 'contributor';\n case 'FIRST_TIMER':\n case 'FIRST_TIME_CONTRIBUTOR':\n case 'NONE':\n return 'unknown';\n case 'MANNEQUIN':\n return 'unknown';\n default:\n return 'unknown';\n }\n}\n\n// ============================================================================\n// Trust Classification\n// ============================================================================\n\n/**\n * Input for trust classification.\n */\nexport interface ClassifyInput {\n /** GitHub username. */\n readonly username: string;\n /** GitHub API author_association value. */\n readonly authorAssociation: string;\n /** Sanitized input (if content has already been through the sanitizer). */\n readonly sanitizedInput?: SanitizedInput;\n /** Sanitizer config (for allowlist check). */\n readonly config?: Partial<SanitizerConfig>;\n}\n\n/**\n * Result of trust classification.\n */\nexport interface ClassifyResult {\n /** Assigned trust tier. */\n readonly trustTier: TrustTier;\n /** GitHub user role. */\n readonly userRole: GitHubUserRole;\n /** Whether the user is on the maintainer allowlist. */\n readonly isAllowlisted: boolean;\n /** Whether content triggered a trust downgrade. */\n readonly wasDowngraded: boolean;\n /** Reason for the assigned tier. */\n readonly reason: string;\n}\n\n/**\n * Classifies a GitHub user and their content into a trust tier.\n *\n * The trust tier is determined by:\n * 1. Allowlist membership (always Tier 1)\n * 2. GitHub author_association → role → default tier\n * 3. Content injection analysis (can only downgrade, never upgrade)\n *\n * ⚠ **Use HostileInputFirewall.process() in agent code paths.** The live\n * paths (`dogfooding/issue-triage`, `dogfooding/pr-reviewer`) route through\n * it as of #4992. Calling classifyTrust() directly emits no audit-trail\n * event, and unless the caller supplies `config.allowlistedMaintainers` no\n * allowlist is consulted — `isAllowlisted: false` is then a default, not a\n * measurement, and must not be recorded as one. Direct use is for unit tests\n * and non-decision analysis only. (The Rule of Two is enforced separately by\n * `evaluatePolicy` in policy-gate; the firewall evaluates it too, as a\n * signal, and refuses on it only under `NEXUS_FIREWALL_POLICY=enforce`.)\n *\n * @see packages/nexus-agents/src/security/firewall/firewall-pipeline.ts\n * @see packages/nexus-agents/src/security/policy-gate.ts\n */\nexport function classifyTrust(input: ClassifyInput): ClassifyResult {\n const allowlistedMaintainers = input.config?.allowlistedMaintainers ?? [];\n const isAllowlisted = allowlistedMaintainers.includes(input.username);\n const userRole = mapAuthorAssociation(input.authorAssociation);\n\n if (isAllowlisted) {\n return {\n trustTier: '1',\n userRole,\n isAllowlisted: true,\n wasDowngraded: false,\n reason: `User ${input.username} is on the maintainer allowlist`,\n };\n }\n\n const baseTier = ROLE_DEFAULT_TRUST[userRole];\n\n // Explicit false means the sanitization stage did not measure content.\n // Absence is legacy sanitizer output and remains measured fail-closed.\n if (input.sanitizedInput !== undefined && input.sanitizedInput.contentTierMeasured !== false) {\n const contentTier = input.sanitizedInput.trustTier;\n const downgraded = TRUST_TIER_NUMERIC[contentTier] > TRUST_TIER_NUMERIC[baseTier];\n\n return {\n trustTier: downgraded ? contentTier : baseTier,\n userRole,\n isAllowlisted: false,\n wasDowngraded: downgraded,\n reason: downgraded\n ? `Downgraded from Tier ${baseTier} to ${contentTier}: injection patterns detected`\n : `Role ${userRole} → Tier ${baseTier}`,\n };\n }\n\n return {\n trustTier: baseTier,\n userRole,\n isAllowlisted: false,\n wasDowngraded: false,\n reason: `Role ${userRole} → Tier ${baseTier}`,\n };\n}\n\n/**\n * Checks whether a trust tier can influence agent decisions.\n * Tiers 3-4 are informational only — they cannot drive actions.\n */\nexport function canInfluenceDecisions(tier: TrustTier): boolean {\n return TRUST_TIER_NUMERIC[tier] <= 2;\n}\n\n/**\n * Checks whether a trust tier requires corroboration with Tier 1 sources.\n * Tier 2 requires corroboration; Tier 1 is self-sufficient.\n */\nexport function requiresCorroboration(tier: TrustTier): boolean {\n return tier === '2';\n}\n\n/**\n * Returns the minimum trust tier required for a given action type.\n * Actions that modify state require higher trust.\n */\nexport function getRequiredTrustTier(actionType: string): TrustTier {\n switch (actionType) {\n case 'GeneratePatchPlan':\n return '1'; // Requires maintainer-level trust\n case 'DraftReply':\n case 'ProposeLabels':\n return '2'; // Requires at least collaborator-level trust\n case 'SummarizeIssue':\n case 'ClassifyIssue':\n case 'IdentifyDuplicates':\n return '3'; // Read-only, can use any input\n case 'RequestHumanApproval':\n case 'RefuseAction':\n return '4'; // Always allowed (safety actions)\n default:\n return '1'; // Unknown actions require highest trust\n }\n}\n","/**\n * nexus-agents/security/firewall - Types\n *\n * Configuration, result, and adapter interface types for the\n * HostileInputFirewall pipeline. Uses Zod schemas for runtime\n * validation at construction boundaries.\n *\n * @module security/firewall/firewall-types\n * (Source: Issue #826 — Reusable Hostile Input Firewall)\n */\n\nimport { z } from 'zod';\nimport type { IAuditLogger } from '../../audit/audit-types.js';\nimport type { FirewallPolicyMode } from './firewall-policy-mode.js';\nimport type {\n ReputationAssessment,\n ReputationGatingMode,\n GitHubUserMetadata,\n} from '../reputation-model.js';\n\n// ============================================================================\n// Source Adapter Interface\n// ============================================================================\n\n/**\n * Metadata extracted from a platform-specific input source.\n */\nexport interface SourceMetadata {\n readonly username: string;\n readonly authorAssociation: string;\n readonly content: string;\n readonly sourceType: string;\n}\n\n/**\n * Adapter that extracts normalized metadata from platform-specific input.\n * Each platform (GitHub, GitLab, etc.) implements this interface.\n */\nexport interface ISourceAdapter {\n readonly platform: string;\n extractMetadata(input: unknown): SourceMetadata;\n}\n\n// ============================================================================\n// Stage Configuration\n// ============================================================================\n\n/**\n * Controls which pipeline stages run. Disabled stages use safe defaults.\n */\nexport const FirewallStagesSchema = z.object({\n sanitization: z.boolean().default(true),\n trustClassification: z.boolean().default(true),\n reputationAssessment: z.boolean().default(false),\n policyEnforcement: z.boolean().default(true),\n corroboration: z.boolean().default(false),\n audit: z.boolean().default(true),\n});\nexport type FirewallStages = z.infer<typeof FirewallStagesSchema>;\n\n/** Creates default stage configuration. */\nexport function createDefaultStages(): FirewallStages {\n return FirewallStagesSchema.parse({});\n}\n\n// ============================================================================\n// Firewall Configuration\n// ============================================================================\n\n/**\n * Configuration for the HostileInputFirewall.\n * The adapter is required; all other fields have sensible defaults.\n */\nexport const FirewallConfigSchema = z.object({\n stages: FirewallStagesSchema.default(() => ({\n sanitization: true,\n trustClassification: true,\n reputationAssessment: false,\n policyEnforcement: true,\n corroboration: false,\n audit: true,\n })),\n allowlistedMaintainers: z.array(z.string().min(1)).default([]),\n maxInputLength: z.number().int().positive().default(50_000),\n context: z\n .object({\n hasWriteAccess: z.boolean().default(false),\n hasSecretAccess: z.boolean().default(false),\n })\n .default(() => ({\n hasWriteAccess: false,\n hasSecretAccess: false,\n })),\n});\n\n/**\n * Full config including the adapter (not Zod-validated since it's an interface).\n */\nexport interface FirewallConfig {\n readonly adapter: ISourceAdapter;\n readonly stages?: Partial<FirewallStages>;\n readonly allowlistedMaintainers?: readonly string[];\n readonly maxInputLength?: number;\n readonly context?: {\n readonly hasWriteAccess?: boolean;\n readonly hasSecretAccess?: boolean;\n };\n /**\n * Rollout gate for behaviour changes to this published API (#5382).\n * Defaults to `NEXUS_FIREWALL_POLICY`, and to `off` when that is unset —\n * under `off` the firewall behaves exactly as it did before #5382.\n *\n * Explicit here as well as in the environment because the firewall is a\n * library: an embedding consumer must be able to opt in per instance without\n * setting a process-wide variable.\n */\n readonly policyMode?: FirewallPolicyMode;\n /**\n * Environment to resolve `policyMode` from when it is not given explicitly.\n * Injectable so the resolution path itself is testable — without this the\n * flag could be unreachable in production with every unit test still passing.\n */\n readonly env?: NodeJS.ProcessEnv;\n /**\n * Optional durable audit logger. When provided, every security decision the\n * firewall records is mirrored to this persistent, hash-chained store via\n * the audit bridge (#3291). When absent, decisions are in-memory only.\n */\n readonly auditLogger?: IAuditLogger;\n /**\n * Rollout gate for REPUTATION demotion (#5381). Distinct from `policyMode`:\n * different env var (`NEXUS_REPUTATION_GATING`) and — load-bearing — a\n * different default, `enforce` rather than `off`. Production reads this same\n * knob (`issue-triage.ts`, `pr-reviewer-helpers.ts`), and the firewall reading\n * a different one is what let the two compositions disagree under identical\n * configuration.\n */\n readonly reputationGatingMode?: ReputationGatingMode;\n /**\n * Supplies the reputation assessment. Defaults to `assessReputation` over the\n * instance's cache.\n *\n * Injectable for the same reason as `env` above: without it the reconciliation\n * is unobservable. The firewall hands the reputation engine only\n * `authorAssociation` + `injectionFlags` — the two inputs the trust classifier\n * already consumed — so reputation is never stricter than the classifier and\n * `reconcileTrustTier` returns the classifier tier every time. Deleting the\n * reconciliation passed 1588 tests (#5405). This seam is what lets a test\n * present a stricter tier and prove the check can fire.\n */\n readonly reputationAssessor?: (metadata: GitHubUserMetadata) => ReputationAssessment;\n /**\n * Whether the sanitizer's content tier is applied as a classification\n * downgrade (#4992). Default `true` — the firewall's behaviour since #826:\n * injection-bearing content from a Tier-2 author classifies as Tier 4.\n *\n * `false` keeps the classifier role-only. The sanitization stage still runs\n * and still records its injection flags — only the tier downgrade is\n * withheld. The dogfooding paths (`issue-triage`, `pr-reviewer`) set this\n * because they route content signals through reputation gating, which has\n * its own rollout knob (`NEXUS_REPUTATION_GATING`); applying the same signal\n * at classification too would bypass that knob and change the recorded\n * `trustTier` under the default `NEXUS_FIREWALL_POLICY=off`, which #5382\n * promises is pass-through.\n */\n readonly contentDowngrade?: boolean;\n}\n\n/**\n * Per-call inputs to {@link HostileInputFirewall.process} (#4992).\n *\n * These are the facts that vary by CALL rather than by instance, so a\n * process-wide firewall (the dogfooding singleton) can serve many repositories\n * and many access postures without holding any of them globally.\n */\nexport interface FirewallProcessOptions {\n /**\n * Maintainer allowlist for THIS call, from the repository context. Replaces —\n * does not merge with — the construction-time list, and is forgotten after\n * the call. When neither this nor the construction-time list was supplied,\n * no allowlist is consulted and `FirewallResult.isAllowlisted` is absent.\n */\n readonly allowlistedMaintainers?: readonly string[];\n /**\n * Access posture of the caller for THIS call, feeding the Rule-of-Two check.\n * Replaces the construction-time `context` for the call. Without it a shared\n * instance would evaluate every caller against one posture, and\n * `wouldRefuse` could never fire for a caller whose posture differs.\n */\n readonly context?: {\n readonly hasWriteAccess: boolean;\n readonly hasSecretAccess: boolean;\n };\n /**\n * The caller's own reputation measurement for THIS call. When present, the\n * reputation gate runs on it under `NEXUS_REPUTATION_GATING`, whether or not\n * the instance's `reputationAssessment` stage is on: `effectiveTrustTier` is\n * the enforced tier, `reputationGate` is returned, and the Rule-of-Two check,\n * `wouldRefuse` and the trust audit event all use that tier. This is what\n * lets a caller with richer signals than the firewall can see (account age,\n * comment history) act on ONE gate rather than two that can disagree.\n *\n * `assessment: undefined` means the caller measured nothing (reputation\n * disabled) but still wants the gate decision recorded on the classifier\n * tier; omitting the option entirely leaves the stage to the instance config.\n */\n readonly reputation?: {\n readonly assessment: ReputationAssessment | undefined;\n };\n}\n\n// ============================================================================\n// Agent Trust Label (ATL) Data\n// ============================================================================\n\n/**\n * Structured data for an Agent Trust Label.\n */\nexport const ATLDataSchema = z.object({\n tier: z.enum(['1', '2', '3', '4']),\n source: z.string().min(1),\n user: z.string().min(1),\n sanitized: z.boolean(),\n rep: z.number().min(0).max(1).optional(),\n});\nexport type ATLData = z.infer<typeof ATLDataSchema>;\n\n// ============================================================================\n// Firewall Error\n// ============================================================================\n\n/**\n * Error codes for firewall pipeline failures.\n */\nexport type FirewallErrorCode =\n | 'EXTRACTION_FAILED'\n | 'SANITIZATION_FAILED'\n | 'CLASSIFICATION_FAILED'\n | 'REPUTATION_FAILED'\n | 'INVALID_CONFIG'\n /**\n * #5382: a blocking policy violation refused the input outright, rather than\n * being surfaced as a signal on a successful result. Only reachable when the\n * firewall policy mode is `enforce` — under the default `off` a violation is\n * still returned via `ruleOfTwoViolation` on an `ok()` result.\n */\n | 'POLICY_REFUSED';\n\n/**\n * Structured error from the firewall pipeline.\n */\nexport interface FirewallError {\n readonly code: FirewallErrorCode;\n readonly message: string;\n readonly stage: string;\n}\n","/**\n * nexus-agents/security/firewall - Agent Trust Labels (ATL)\n *\n * Generates and parses structured trust labels that travel with\n * processed inputs through the agent pipeline. Format:\n * [ATL:tier=3,source=github-comment,user=octocat,sanitized=true,rep=0.45]\n *\n * @module security/firewall/agent-trust-labels\n * (Source: Issue #826 — Reusable Hostile Input Firewall)\n */\n\nimport type { ATLData } from './firewall-types.js';\nimport { ATLDataSchema } from './firewall-types.js';\n\n/** ATL prefix and suffix markers. */\nconst ATL_PREFIX = '[ATL:';\nconst ATL_SUFFIX = ']';\n\n/** Pattern to match and extract ATL strings. */\nconst ATL_PATTERN = /^\\[ATL:(.+)\\]$/;\n\n/**\n * Generates an Agent Trust Label string from structured data.\n *\n * @example\n * generateATL({ tier: '3', source: 'github-comment', user: 'octocat', sanitized: true })\n * // => \"[ATL:tier=3,source=github-comment,user=octocat,sanitized=true]\"\n */\nexport function generateATL(data: ATLData): string {\n const validated = ATLDataSchema.parse(data);\n const parts: string[] = [\n `tier=${validated.tier}`,\n `source=${encodeATLValue(validated.source)}`,\n `user=${encodeATLValue(validated.user)}`,\n `sanitized=${String(validated.sanitized)}`,\n ];\n\n if (validated.rep !== undefined) {\n parts.push(`rep=${validated.rep.toFixed(2)}`);\n }\n\n return `${ATL_PREFIX}${parts.join(',')}${ATL_SUFFIX}`;\n}\n\n/**\n * Parses an ATL string back into structured data.\n * Returns undefined if the string is not a valid ATL.\n */\nexport function parseATL(atl: string): ATLData | undefined {\n const match = ATL_PATTERN.exec(atl.trim());\n if (match === null) return undefined;\n\n const body = match[1];\n if (body === undefined) return undefined;\n\n const raw = parseKeyValuePairs(body);\n if (raw === undefined) return undefined;\n\n return validateParsedATL(raw);\n}\n\n/** Parses comma-separated key=value pairs into a record. */\nfunction parseKeyValuePairs(body: string): Record<string, string> | undefined {\n const pairs = body.split(',');\n const raw: Record<string, string> = {};\n for (const pair of pairs) {\n const eqIndex = pair.indexOf('=');\n if (eqIndex < 1) return undefined;\n raw[pair.slice(0, eqIndex)] = pair.slice(eqIndex + 1);\n }\n return raw;\n}\n\n/** Validates and converts raw key-value pairs to ATLData. */\nfunction validateParsedATL(raw: Record<string, string>): ATLData | undefined {\n const tier = raw['tier'];\n const source = raw['source'];\n const user = raw['user'];\n const sanitized = raw['sanitized'];\n if (tier === undefined || source === undefined || user === undefined || sanitized === undefined) {\n return undefined;\n }\n\n const parsed: ATLData = {\n tier: tier as ATLData['tier'],\n source: decodeATLValue(source),\n user: decodeATLValue(user),\n sanitized: sanitized === 'true',\n ...(raw['rep'] !== undefined ? { rep: parseFloat(raw['rep']) } : {}),\n };\n\n const result = ATLDataSchema.safeParse(parsed);\n return result.success ? result.data : undefined;\n}\n\n/** Encodes special characters in ATL values. */\nfunction encodeATLValue(value: string): string {\n return value.replace(/,/g, '%2C').replace(/=/g, '%3D').replace(/\\]/g, '%5D');\n}\n\n/** Decodes special characters in ATL values. */\nfunction decodeATLValue(value: string): string {\n return value.replace(/%2C/g, ',').replace(/%3D/g, '=').replace(/%5D/g, ']');\n}\n","/**\n * nexus-agents/security/firewall - Passthrough defaults for disabled stages\n *\n * Split out of the pipeline so it stays under the file-size lint (#4992).\n *\n * @module security/firewall/firewall-passthrough\n */\n\nimport { mapAuthorAssociation } from '../trust-classifier.js';\nimport type { ClassifyResult } from '../trust-classifier.js';\nimport { ROLE_DEFAULT_TRUST } from '../trust-types.js';\nimport type { SanitizedInput } from '../trust-types.js';\nimport type { SourceMetadata } from './firewall-types.js';\n\nexport function createPassthroughSanitized(meta: SourceMetadata): SanitizedInput {\n const userRole = mapAuthorAssociation(meta.authorAssociation);\n return {\n content: meta.content,\n originalLength: meta.content.length,\n trustTier: ROLE_DEFAULT_TRUST[userRole],\n contentTierMeasured: false,\n userRole,\n injectionFlags: [],\n strippedElements: [],\n truncated: false,\n wasModified: false,\n sanitizedAt: new Date().toISOString(),\n };\n}\n\nexport function createPassthroughClassification(meta: SourceMetadata): ClassifyResult {\n return {\n trustTier: '3',\n userRole: mapAuthorAssociation(meta.authorAssociation),\n isAllowlisted: false,\n wasDowngraded: false,\n reason: 'Trust classification disabled — default Tier 3',\n };\n}\n","/**\n * nexus-agents/security/firewall - Trust audit reason\n *\n * The trust audit event's `reason` must state what the reputation gate did —\n * or, under `audit`, what it would have done (#4992 review). A suppressed\n * demotion that leaves no trace reads as a clean classification.\n *\n * @module security/firewall/firewall-trust-reason\n */\n\nimport type { ReputationGateDecision } from '../reputation-model.js';\n\n/**\n * Appends the gate's effect to the classifier's reason.\n *\n * @param reason The classifier's own reason.\n * @param demoted Whether the enforced tier differs from the classifier tier.\n * @param gate The gate decision, or `undefined` when no gate ran.\n */\nexport function describeGate(\n reason: string,\n demoted: boolean,\n gate: ReputationGateDecision | undefined\n): string {\n if (gate === undefined) return reason;\n if (demoted) {\n return `${reason}; demoted to Tier ${gate.enforcedTier} by reputation gating (${gate.mode})`;\n }\n if (gate.demotionSuppressed) {\n return `${reason}; would demote to Tier ${gate.reconciledTier} (reputation gating: ${gate.mode})`;\n }\n return reason;\n}\n","/**\n * nexus-agents/security - Policy Gate\n *\n * Deterministic rule engine that validates all agent actions before\n * GitHub state mutations can occur. No LLM in the validation path.\n *\n * Defense layer 2 of the three-layer hardening architecture.\n * See: docs/architecture/UNTRUSTED_INPUT_HARDENING.md\n *\n * @module security/policy-gate\n * (Source: Issue #818, #822 — Phase 2: Policy Gate)\n */\n\nimport { z } from 'zod';\n\nimport type { AgentAction, AgentActionType, SourceCitation } from './action-schema.js';\nimport {\n isMutatingAction,\n isReadOnlyAction,\n requiresCitation,\n requiresHumanApproval,\n} from './action-schema.js';\nimport type { TrustTier } from './trust-types.js';\nimport { TRUST_TIER_NUMERIC } from './trust-types.js';\nimport { getRequiredTrustTier, canInfluenceDecisions } from './trust-classifier.js';\nimport type { AuditTrail } from './audit-trail.js';\nimport { emitPolicyEvent } from './audit-trail.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Violation detected by the policy gate.\n */\nexport const ViolationSchema = z.object({\n /** Machine-readable rule identifier. */\n rule: z.string().min(1),\n /** Human-readable description of the violation. */\n message: z.string().min(1),\n /** Severity: 'block' prevents execution, 'warn' logs only. */\n severity: z.enum(['block', 'warn']),\n});\nexport type Violation = z.infer<typeof ViolationSchema>;\n\n/**\n * Decision returned by the policy gate.\n */\nexport interface PolicyDecision {\n /** Whether the action is allowed to proceed. */\n readonly allowed: boolean;\n /** Whether human approval is required before execution. */\n readonly requiresApproval: boolean;\n /** All detected violations (blocking and warnings). */\n readonly violations: readonly Violation[];\n /** Timestamp of the evaluation (ISO 8601). */\n readonly evaluatedAt: string;\n}\n\n/**\n * Context for evaluating a policy decision.\n */\nexport interface ActionContext {\n /** Trust tier of the primary input source. */\n readonly inputTrustTier: TrustTier;\n /** Whether the agent currently has write access to the repository. */\n readonly hasWriteAccess: boolean;\n /** Whether the agent currently has access to secrets/tokens. */\n readonly hasSecretAccess: boolean;\n /** Set of labels that exist on the repository (for ProposeLabels validation). */\n readonly existingLabels?: ReadonlySet<string>;\n}\n\n// ============================================================================\n// Policy Rules\n// ============================================================================\n\n/** Extract sources from an action, handling actions that lack a sources field. */\nfunction getActionSources(action: AgentAction): readonly SourceCitation[] {\n if ('sources' in action) {\n return action.sources;\n }\n return [];\n}\n\n/** Check that citations exist for actions that require them. */\nfunction checkCitationRequirement(action: AgentAction): Violation | undefined {\n if (!requiresCitation(action.type)) return undefined;\n const sources = getActionSources(action);\n if (sources.length === 0) {\n return {\n rule: 'REQUIRE_CITATION',\n message: `Action '${action.type}' requires at least one source citation`,\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/** Check that the input trust tier meets action requirements. */\nfunction checkTrustRequirement(action: AgentAction, context: ActionContext): Violation | undefined {\n const requiredTier = getRequiredTrustTier(action.type);\n const requiredNumeric = TRUST_TIER_NUMERIC[requiredTier];\n const inputNumeric = TRUST_TIER_NUMERIC[context.inputTrustTier];\n\n if (inputNumeric > requiredNumeric) {\n return {\n rule: 'INSUFFICIENT_TRUST',\n message: `Action '${action.type}' requires Tier ${requiredTier} but input is Tier ${context.inputTrustTier}`,\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/** Check that Tier 3-4 input cannot drive decisions. */\nfunction checkInfluenceBlock(action: AgentAction, context: ActionContext): Violation | undefined {\n if (!canInfluenceDecisions(context.inputTrustTier) && isMutatingAction(action.type)) {\n return {\n rule: 'UNTRUSTED_INFLUENCE',\n message: `Tier ${context.inputTrustTier} input cannot drive mutating action '${action.type}'`,\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/**\n * Enforce the Rule of Two: no agent may simultaneously\n * (a) process untrusted input, (b) have write access, AND (c) access secrets.\n *\n * Exported (#3198) so the firewall's `policyEnforcement` stage can surface the\n * same assessment during input composition without duplicating the predicate.\n */\nexport function checkRuleOfTwo(context: ActionContext): Violation | undefined {\n const isUntrusted = TRUST_TIER_NUMERIC[context.inputTrustTier] >= 3;\n if (isUntrusted && context.hasWriteAccess && context.hasSecretAccess) {\n return {\n rule: 'RULE_OF_TWO',\n message:\n 'Rule of Two violation: agent simultaneously processes untrusted input, has write access, and accesses secrets',\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/**\n * Labels that GRANT PRIVILEGE or REDUCE SCRUTINY, and so may never be proposed\n * by an agent acting on external input (#4688).\n *\n * These are not ordinary taxonomy labels. In this repository they are control\n * inputs to CI:\n *\n * - `owner-ratified` — the label `check-governor-ratification.ts` accepts as\n * proof of owner ratification. Applying it BYPASSES the governance-of-the-\n * governor gate on `src/audit/`, `.rules/`, `CODEOWNERS` and friends.\n * - `skip-pr-review` — suppresses the PR review workflow.\n * - `pr-review-ci` — triggers the review workflow; less dangerous than the\n * two above, but still a workflow control rather than a description.\n *\n * Why this exists BEFORE it is needed: no production code applies a proposed\n * label today (`addLabels` has no non-test caller), so the path is latent. But\n * the consensus panel's dissenting seat was right that labels are not\n * zero-blast-radius, and the day someone wires triage output to `addLabels` is\n * the day an issue body can propose its own ratification. A guard added after\n * that wiring is a guard added after the incident.\n *\n * This is deliberately checked on the ACTION's effect rather than the author's\n * trust: an OWNER-authored body proposing `owner-ratified` is precisely the\n * self-modification the governor exists to prevent, so author trust must not\n * soften it.\n */\nconst PRIVILEGE_GRANTING_LABELS: ReadonlySet<string> = new Set([\n 'owner-ratified',\n 'skip-pr-review',\n 'pr-review-ci',\n]);\n\n/**\n * Refuse any proposal naming a privilege-granting label.\n *\n * Independent of {@link checkLabelValidity} on purpose: this check needs no\n * repository world state and reports privileged labels even when label\n * validity is unevaluated.\n */\nfunction checkPrivilegedLabels(action: AgentAction): Violation | undefined {\n if (action.type !== 'ProposeLabels') return undefined;\n\n // Case-insensitive, because the gate this protects is (#4689 follow-up):\n // `check-governor-ratification.ts` compares `l.toLowerCase()`, so\n // `Owner-Ratified` would satisfy ratification while slipping an exact-match\n // denylist. A guard must be at least as permissive in what it REJECTS as its\n // consumer is in what it ACCEPTS.\n const privileged = action.labels.filter((l) => PRIVILEGE_GRANTING_LABELS.has(l.toLowerCase()));\n if (privileged.length === 0) return undefined;\n\n return {\n rule: 'PRIVILEGED_LABEL',\n message:\n `Proposed labels grant privilege or reduce review scrutiny: ` +\n `${privileged.join(', ')}. These are CI control inputs, not descriptions, ` +\n `and are never proposable from agent-processed input (#4688).`,\n severity: 'block',\n };\n}\n\n/** Check that proposed labels exist in the repository's label set. */\nfunction checkLabelValidity(action: AgentAction, context: ActionContext): Violation | undefined {\n if (action.type !== 'ProposeLabels') return undefined;\n const labels = context.existingLabels;\n if (labels === undefined) {\n return {\n rule: 'LABEL_SET_UNAVAILABLE',\n message: 'repository label set not supplied; label validity unevaluated',\n severity: 'block',\n };\n }\n\n const invalid = action.labels.filter((l) => !labels.has(l));\n if (invalid.length > 0) {\n return {\n rule: 'INVALID_LABELS',\n message: `Proposed labels not in repository: ${invalid.join(', ')}`,\n severity: 'block',\n };\n }\n return undefined;\n}\n\n/** Check that source citations meet trust requirements for the action. */\nfunction checkSourceTrustTiers(action: AgentAction): Violation | undefined {\n const sources = getActionSources(action);\n if (sources.length === 0) return undefined;\n\n const requiredTier = getRequiredTrustTier(action.type);\n const requiredNumeric = TRUST_TIER_NUMERIC[requiredTier];\n\n for (const source of sources) {\n if (source.type === 'issueComment') {\n const sourceTierNumeric = TRUST_TIER_NUMERIC[source.authorTrustTier];\n if (sourceTierNumeric > requiredNumeric) {\n return {\n rule: 'SOURCE_TRUST_MISMATCH',\n message: `Source from '${source.author}' (Tier ${source.authorTrustTier}) insufficient for action requiring Tier ${requiredTier}`,\n severity: 'warn',\n };\n }\n }\n }\n return undefined;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Evaluate an agent action against the policy gate.\n *\n * This is a deterministic check — no LLM in the loop. Returns a\n * PolicyDecision indicating whether the action is allowed, requires\n * human approval, or is blocked.\n *\n * @param action - The validated AgentAction to evaluate.\n * @param context - The current execution context.\n * @returns PolicyDecision with violations and approval requirements.\n */\nexport function evaluatePolicy(\n action: AgentAction,\n context: ActionContext,\n auditTrail?: AuditTrail\n): PolicyDecision {\n const violations: Violation[] = [];\n\n const checks = [\n checkCitationRequirement(action),\n checkTrustRequirement(action, context),\n checkInfluenceBlock(action, context),\n checkRuleOfTwo(context),\n checkLabelValidity(action, context),\n checkPrivilegedLabels(action),\n checkSourceTrustTiers(action),\n ];\n\n for (const violation of checks) {\n if (violation !== undefined) {\n violations.push(violation);\n }\n }\n\n const hasBlockingViolation = violations.some((v) => v.severity === 'block');\n // #4463: gate on irreversibility + external visibility, not on mutation as\n // such. ProposeLabels and GeneratePatchPlan are reversible and internal, and\n // reach here having already cleared citation, trust-tier, influence-block,\n // Rule-of-Two and label-validity checks. DraftReply still gates because it\n // publishes text under the project's identity on a surface others read.\n // `isMutatingAction` stays broad above (line ~113) for the influence block:\n // low-trust input must not drive ANY mutating action, approved or not.\n const needsApproval = !hasBlockingViolation && requiresHumanApproval(action.type);\n\n const decision: PolicyDecision = {\n allowed: !hasBlockingViolation,\n requiresApproval: needsApproval,\n violations,\n evaluatedAt: new Date().toISOString(),\n };\n\n // #3191: when an audit trail is supplied, record the decision so policy\n // outcomes are part of the durable audit record (the gate previously emitted\n // nothing). Optional — pure callers pass no trail and incur no side effect.\n if (auditTrail !== undefined) {\n emitPolicyEvent(auditTrail, {\n actionType: action.type,\n allowed: decision.allowed,\n requiresApproval: decision.requiresApproval,\n inputTrustTier: context.inputTrustTier,\n violationRules: violations.map((v) => v.rule),\n });\n }\n\n return decision;\n}\n\n/**\n * Quick check: can this action type proceed at all given the input trust tier?\n * Useful for early rejection before full policy evaluation.\n */\nexport function canProceed(actionType: AgentActionType, inputTrustTier: TrustTier): boolean {\n if (isReadOnlyAction(actionType)) {\n const requiredTier = getRequiredTrustTier(actionType);\n return TRUST_TIER_NUMERIC[inputTrustTier] <= TRUST_TIER_NUMERIC[requiredTier];\n }\n return canInfluenceDecisions(inputTrustTier);\n}\n","/**\n * nexus-agents/security - Typed Action Schema\n *\n * Zod schemas and TypeScript types for the typed action constraint system.\n * Agents processing untrusted GitHub input MUST emit only predefined typed\n * actions (never free-form tool calls). This module defines those action\n * schemas, validates them at runtime, and classifies them as read-only or\n * mutating for the policy gate.\n *\n * @module security/action-schema\n * (Source: Issue #818, #820)\n */\n\nimport { z } from 'zod';\n\nimport { TrustTierSchema } from './trust-types.js';\n\n// ============================================================================\n// Result Type (local — avoids circular dependency with core/types)\n// ============================================================================\n\n/** Validation result using the project Result pattern. */\nexport type ActionValidationResult =\n { ok: true; value: AgentAction } | { ok: false; error: string };\n\n// ============================================================================\n// Source Citations\n// ============================================================================\n\n/** Source citation from a file tracked in the repository. */\nconst RepoFileSource = z.object({\n type: z.literal('repoFile'),\n path: z.string().min(1),\n line: z.number().int().positive().optional(),\n commit: z\n .string()\n .regex(/^[a-f0-9]{7,40}$/)\n .optional(),\n});\n\n/** Source citation from a GitHub issue comment. */\nconst IssueCommentSource = z.object({\n type: z.literal('issueComment'),\n issueNumber: z.number().int().positive(),\n commentId: z.number().int().positive(),\n author: z.string().min(1),\n authorTrustTier: TrustTierSchema,\n});\n\n/**\n * Source citation from the body of a GitHub issue (#4667).\n *\n * Triage previously cited the issue as a `repoFile` with a synthesised path\n * (`issues/42`). That is not a repo file, and the mislabel is why corroboration\n * always passed: `hasSourceAtTier` treats repo files as Tier 1 unconditionally,\n * so untrusted issue text was corroborating actions at maintainer trust.\n *\n * Modelled on `IssueCommentSource` — it carries the author and their tier, so\n * the trust of the content travels with the citation. It is deliberately NOT an\n * `issueComment`: that requires a `commentId`, and faking one to fix a mislabel\n * would trade an honest error for a dishonest one.\n */\nconst IssueBodySource = z.object({\n type: z.literal('issueBody'),\n issueNumber: z.number().int().positive(),\n author: z.string().min(1),\n authorTrustTier: TrustTierSchema,\n});\n\n/** Source citation from a CI pipeline result. */\nconst CIResultSource = z.object({\n type: z.literal('ciResult'),\n runId: z.number().int().positive(),\n status: z.enum(['pass', 'fail']),\n job: z.string().min(1),\n});\n\n/** Source citation from a policy or governance document. */\nconst PolicyDocSource = z.object({\n type: z.literal('policyDoc'),\n path: z.string().min(1),\n section: z.string().min(1),\n});\n\n/** Source citation from a maintainer slash-command or explicit instruction. */\nconst MaintainerCommandSource = z.object({\n type: z.literal('maintainerCommand'),\n username: z.string().min(1),\n commentId: z.number().int().positive(),\n});\n\n/**\n * Discriminated union of all valid source citation types.\n * Every decision-making action MUST cite at least one source.\n */\nexport const SourceCitationSchema = z.discriminatedUnion('type', [\n IssueBodySource,\n RepoFileSource,\n IssueCommentSource,\n CIResultSource,\n PolicyDocSource,\n MaintainerCommandSource,\n]);\n\n/** Inferred TypeScript type for a source citation. */\nexport type SourceCitation = z.infer<typeof SourceCitationSchema>;\n\n// ============================================================================\n// Typed Actions\n// ============================================================================\n\n/** Read-only analysis action: summarize an issue with citations. */\nconst SummarizeIssueAction = z.object({\n type: z.literal('SummarizeIssue'),\n summary: z.string().min(10).max(2000),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Suggest labels for an issue (max 5, must match existing label set). */\nconst ProposeLabelsAction = z.object({\n type: z.literal('ProposeLabels'),\n labels: z.array(z.string()).min(1).max(5),\n reason: z.string().min(10).max(500),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Draft a reply comment (always requires human approval before posting). */\nexport const DRAFT_REPLY_BODY_MAX_LENGTH = 2000;\n\nconst DraftReplyAction = z.object({\n type: z.literal('DraftReply'),\n body: z.string().min(10).max(DRAFT_REPLY_BODY_MAX_LENGTH),\n requiresApproval: z.literal(true),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Explicit escalation to a human maintainer with reason and context. */\nconst RequestHumanApprovalAction = z.object({\n type: z.literal('RequestHumanApproval'),\n reason: z.string().min(10).max(500),\n context: z.string().min(10).max(2000),\n});\n\n/**\n * Propose a set of file modifications (requires maintainer corroboration).\n * Forbidden from Tier 3-4 input without maintainer corroboration.\n */\nconst GeneratePatchPlanAction = z.object({\n type: z.literal('GeneratePatchPlan'),\n files: z\n .array(\n z.object({\n path: z.string().min(1),\n operation: z.enum(['modify', 'create', 'delete']),\n description: z.string().min(10).max(500),\n })\n )\n .min(1)\n .max(10),\n rationale: z.string().min(10).max(1000),\n requiresApproval: z.literal(true),\n sources: z.array(SourceCitationSchema).min(2).max(20),\n});\n\n/** Categorize an issue by type with confidence score. */\nconst ClassifyIssueAction = z.object({\n type: z.literal('ClassifyIssue'),\n category: z.enum(['bug', 'feature', 'question', 'documentation', 'security', 'performance']),\n confidence: z.number().min(0).max(1),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Identify potential duplicate issues with similarity scores. */\nconst IdentifyDuplicatesAction = z.object({\n type: z.literal('IdentifyDuplicates'),\n candidates: z.array(z.number().int().positive()).min(1).max(10),\n similarity: z.array(z.number().min(0).max(1)),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/** Explicit refusal to act, with escalation target. */\nconst RefuseActionAction = z.object({\n type: z.literal('RefuseAction'),\n reason: z.string().min(10).max(500),\n escalateTo: z.enum(['maintainer', 'security']),\n});\n\n/** Handoff delegation from one agent to another by capability. (Issue #834) */\nconst HandoffMessageAction = z.object({\n type: z.literal('HandoffMessage'),\n targetCapability: z.string().min(1).max(100),\n reason: z.string().min(5).max(500),\n inputTrustTier: z.enum(['1', '2', '3', '4']),\n sources: z.array(SourceCitationSchema).min(1).max(20),\n});\n\n/**\n * Discriminated union of all valid agent actions.\n * This is the ONLY schema agents may emit when processing untrusted input.\n */\nexport const AgentActionSchema = z.discriminatedUnion('type', [\n SummarizeIssueAction,\n ProposeLabelsAction,\n DraftReplyAction,\n RequestHumanApprovalAction,\n GeneratePatchPlanAction,\n ClassifyIssueAction,\n IdentifyDuplicatesAction,\n RefuseActionAction,\n HandoffMessageAction,\n]);\n\n/** Inferred TypeScript type for an agent action. */\nexport type AgentAction = z.infer<typeof AgentActionSchema>;\n\n/** All valid action type discriminator values. */\nexport type AgentActionType = AgentAction['type'];\n\n// ============================================================================\n// Action Classification\n// ============================================================================\n\n/**\n * Action types that are read-only and do not modify GitHub state.\n * These can be executed without human approval (subject to policy gate).\n */\nconst READ_ONLY_ACTIONS: ReadonlySet<AgentActionType> = new Set<AgentActionType>([\n 'SummarizeIssue',\n 'ClassifyIssue',\n 'IdentifyDuplicates',\n 'RefuseAction',\n 'RequestHumanApproval',\n 'HandoffMessage',\n]);\n\n/**\n * Action types that can modify GitHub state (labels, comments, code).\n *\n * Used for the untrusted-input influence block: low-trust input must not drive\n * ANY of these, approved or not. NOT the approval set — see\n * {@link APPROVAL_REQUIRED_ACTIONS}, which is narrower (#4463).\n */\nconst MUTATING_ACTIONS: ReadonlySet<AgentActionType> = new Set<AgentActionType>([\n 'ProposeLabels',\n 'DraftReply',\n 'GeneratePatchPlan',\n]);\n\n/**\n * Action types that MUST include at least one source citation.\n * Escalation and refusal actions are exempt (they report inability to act).\n */\nconst CITATION_REQUIRED_ACTIONS: ReadonlySet<AgentActionType> = new Set<AgentActionType>([\n 'SummarizeIssue',\n 'ProposeLabels',\n 'DraftReply',\n 'GeneratePatchPlan',\n 'ClassifyIssue',\n 'IdentifyDuplicates',\n 'HandoffMessage',\n]);\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Validate an unknown value against the AgentActionSchema.\n * Returns a Result: `{ ok: true; value }` on success or `{ ok: false; error }` on failure.\n *\n * @param input - The value to validate (typically parsed JSON from an agent).\n * @returns Validation result following the project Result pattern.\n */\nexport function validateAgentAction(input: unknown): ActionValidationResult {\n const result = AgentActionSchema.safeParse(input);\n if (result.success) {\n return { ok: true, value: result.data };\n }\n const messages = result.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);\n return { ok: false, error: `Action validation failed: ${messages.join('; ')}` };\n}\n\n/**\n * Check whether an action type is read-only (does not modify GitHub state).\n *\n * @param actionType - The action type discriminator value.\n * @returns True if the action is read-only.\n */\nexport function isReadOnlyAction(actionType: AgentActionType): boolean {\n return READ_ONLY_ACTIONS.has(actionType);\n}\n\n/**\n * Check whether an action type can modify GitHub state.\n * Mutating actions always require human approval before execution.\n *\n * @param actionType - The action type discriminator value.\n * @returns True if the action can modify state.\n */\nexport function isMutatingAction(actionType: AgentActionType): boolean {\n return MUTATING_ACTIONS.has(actionType);\n}\n\n/**\n * Action types that still require human approval after every policy check has\n * passed (#4463).\n *\n * Deliberately narrower than {@link MUTATING_ACTIONS}. Approval is a gate on\n * IRREVERSIBILITY and EXTERNAL VISIBILITY, not on mutation as such:\n *\n * - `ProposeLabels` — reversible in one click, internal to the tracker, and its\n * schema carries no `requiresApproval` field. Not gated: it reaches this point\n * having already passed citation, trust-tier, influence-block, Rule-of-Two and\n * label-validity checks, so pausing bought delay, not safety.\n * - `DraftReply` — **publishes text under the project's identity**, on a surface\n * other people read. Untrusted issue/PR content is in scope by definition\n * here, so a prompt-injected reply is attacker-authored text posted publicly.\n * Deleting it afterwards does not un-publish it.\n * - `GeneratePatchPlan` — gated, and deliberately so: its own schema encodes\n * `requiresApproval: z.literal(true)` alongside a two-source corroboration\n * minimum (`sources.min(2)`). Dropping the gate here while the schema still\n * mandates the flag would put two sources of truth in conflict, and the\n * two-factor design is intentional for an action that proposes file writes\n * from potentially untrusted input.\n */\nconst APPROVAL_REQUIRED_ACTIONS: ReadonlySet<AgentActionType> = new Set<AgentActionType>([\n 'DraftReply',\n 'GeneratePatchPlan',\n]);\n\n/**\n * Whether an action needs human approval even after passing every policy check.\n *\n * Distinct from {@link isMutatingAction}, which stays broad because it also\n * drives the untrusted-input influence block — low-trust input must not be able\n * to drive ANY mutating action, approved or not.\n *\n * @param actionType - The action type discriminator value.\n * @returns True if a human must approve before execution.\n */\nexport function requiresHumanApproval(actionType: AgentActionType): boolean {\n return APPROVAL_REQUIRED_ACTIONS.has(actionType);\n}\n\n/**\n * Check whether an action type requires at least one source citation.\n * Escalation (RequestHumanApproval) and refusal (RefuseAction) are exempt.\n *\n * @param actionType - The action type discriminator value.\n * @returns True if the action must include source citations.\n */\nexport function requiresCitation(actionType: AgentActionType): boolean {\n return CITATION_REQUIRED_ACTIONS.has(actionType);\n}\n","/**\n * nexus-agents/security - Corroboration Validator\n *\n * Validates that agent decisions are backed by sufficient evidence from\n * authoritative sources. Each action type has specific corroboration\n * requirements (e.g., closing issues requires CI pass or maintainer comment).\n *\n * Defense layer 3 of the three-layer hardening architecture.\n * See: docs/architecture/UNTRUSTED_INPUT_HARDENING.md\n *\n * @module security/corroboration-validator\n * (Source: Issue #818, #823 — Phase 2: Corroboration Validator)\n */\n\nimport type { AgentAction, AgentActionType, SourceCitation } from './action-schema.js';\nimport type { TrustTier } from './trust-types.js';\nimport { TRUST_TIER_NUMERIC } from './trust-types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Result of corroboration validation.\n */\nexport interface CorroborationResult {\n /** Whether corroboration requirements are satisfied. */\n readonly satisfied: boolean;\n /** Sources that contributed to corroboration. */\n readonly corroboratingSources: readonly SourceCitation[];\n /** Missing corroboration requirements (empty when satisfied). */\n readonly missing: readonly string[];\n /** Action type that was validated. */\n readonly actionType: AgentActionType;\n}\n\n/**\n * Rule defining what corroboration an action requires.\n */\nexport interface CorroborationRule {\n /** Human-readable description of what's required. */\n readonly description: string;\n /** Predicate: does this set of sources satisfy the requirement? */\n readonly isSatisfied: (sources: readonly SourceCitation[]) => boolean;\n}\n\n// ============================================================================\n// Source Helpers\n// ============================================================================\n\n/** Check if sources include a passing CI result. */\nfunction hasCIPass(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'ciResult' && s.status === 'pass');\n}\n\n/** Check if sources include a maintainer command. */\nfunction hasMaintainerCommand(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'maintainerCommand');\n}\n\n/** Check if sources include a Tier 1 issue comment. */\nfunction hasTier1Comment(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'issueComment' && s.authorTrustTier === '1');\n}\n\n/** Check if sources include a repo file reference. */\nfunction hasRepoFileRef(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'repoFile');\n}\n\n/** Check if sources include a repo file with line reference. */\nfunction hasCodeLevelEvidence(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'repoFile' && s.line !== undefined);\n}\n\n/** Check if sources include a policy document reference. */\nfunction hasPolicyDocRef(sources: readonly SourceCitation[]): boolean {\n return sources.some((s) => s.type === 'policyDoc');\n}\n\n/**\n * True when an ISSUE BODY source is authored at or above `maxTier` (#4667).\n *\n * Deliberately narrower than {@link hasSourceAtTier}, which returns `true` for\n * any non-authored source — repo files, CI results and policy docs are all\n * Tier 1 there. Reusing it here made a CI result alone satisfy ProposeLabels,\n * which the corroboration tests correctly forbid.\n */\nfunction hasIssueBodyAtTier(sources: readonly SourceCitation[], maxTier: TrustTier): boolean {\n const maxNumeric = TRUST_TIER_NUMERIC[maxTier];\n return sources.some(\n (s) => s.type === 'issueBody' && TRUST_TIER_NUMERIC[s.authorTrustTier] <= maxNumeric\n );\n}\n\n/** Check if at least one source meets a minimum trust tier. */\nfunction hasSourceAtTier(sources: readonly SourceCitation[], maxTier: TrustTier): boolean {\n const maxNumeric = TRUST_TIER_NUMERIC[maxTier];\n return sources.some((s) => {\n // Author-attributed content carries its author's trust, not the repo's.\n // `issueBody` was previously cited as a `repoFile` and so counted as Tier 1\n // unconditionally — untrusted issue text corroborating at maintainer trust\n // (#4667).\n if (s.type === 'issueComment' || s.type === 'issueBody') {\n return TRUST_TIER_NUMERIC[s.authorTrustTier] <= maxNumeric;\n }\n // Repo files, CI results, policy docs, maintainer commands = Tier 1\n return true;\n });\n}\n\n// ============================================================================\n// Per-Action Corroboration Rules\n// ============================================================================\n\n/**\n * Corroboration rules for each action type.\n * See CLAUDE.md \"Corroboration Requirements\" table.\n */\nconst ACTION_CORROBORATION_RULES: Readonly<Record<AgentActionType, readonly CorroborationRule[]>> =\n {\n SummarizeIssue: [\n {\n description: 'At least one Tier 1/2 source',\n isSatisfied: (s) => hasSourceAtTier(s, '2'),\n },\n ],\n ProposeLabels: [\n {\n // #4667: the old description read \"issue body (repo file)\" — the rule\n // was written when triage cited the issue as a `repoFile`, so an issue\n // body satisfied it unconditionally regardless of who wrote it. Now\n // that the citation is honest (`issueBody`, carrying its author's\n // tier), the rule has to say what it always meant: an issue body\n // corroborates a label proposal only from a sufficiently trusted\n // author. Dropping the clause entirely would make this rule\n // unsatisfiable for every author including the repo owner — trading a\n // check that could never fail for one that can never pass.\n description:\n 'Issue body from a Tier 1/2 author, OR repo file / maintainer instruction / policy doc',\n isSatisfied: (s) =>\n hasIssueBodyAtTier(s, '2') ||\n hasRepoFileRef(s) ||\n hasMaintainerCommand(s) ||\n hasPolicyDocRef(s),\n },\n ],\n DraftReply: [\n {\n description: 'At least one Tier 1 source citation',\n isSatisfied: (s) =>\n hasRepoFileRef(s) ||\n hasCIPass(s) ||\n hasMaintainerCommand(s) ||\n hasPolicyDocRef(s) ||\n hasTier1Comment(s),\n },\n ],\n GeneratePatchPlan: [\n {\n description: 'Failing test OR bug reproduction steps (code-level evidence)',\n isSatisfied: (s) => hasCodeLevelEvidence(s) || hasCIPass(s),\n },\n {\n description: 'Maintainer corroboration',\n isSatisfied: (s) => hasMaintainerCommand(s) || hasTier1Comment(s),\n },\n ],\n ClassifyIssue: [\n {\n description: 'At least one source citation',\n isSatisfied: (s) => s.length > 0,\n },\n ],\n IdentifyDuplicates: [\n {\n description: 'At least one source citation',\n isSatisfied: (s) => s.length > 0,\n },\n ],\n RequestHumanApproval: [],\n RefuseAction: [],\n HandoffMessage: [\n {\n description: 'At least one source citation for trust tier propagation',\n isSatisfied: (s) => s.length > 0,\n },\n ],\n };\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Validate that an agent action has sufficient corroboration from\n * authoritative sources.\n *\n * @param action - The validated AgentAction to check.\n * @returns CorroborationResult indicating whether requirements are met.\n */\nexport function validateCorroboration(action: AgentAction): CorroborationResult {\n const rules = ACTION_CORROBORATION_RULES[action.type];\n const sources: readonly SourceCitation[] = 'sources' in action ? action.sources : [];\n\n if (rules.length === 0) {\n return {\n satisfied: true,\n corroboratingSources: sources,\n missing: [],\n actionType: action.type,\n };\n }\n\n const missing: string[] = [];\n const corroborating: SourceCitation[] = [];\n\n for (const rule of rules) {\n if (rule.isSatisfied(sources)) {\n for (const s of sources) {\n if (!corroborating.includes(s)) {\n corroborating.push(s);\n }\n }\n } else {\n missing.push(rule.description);\n }\n }\n\n return {\n satisfied: missing.length === 0,\n corroboratingSources: corroborating,\n missing,\n actionType: action.type,\n };\n}\n\n/**\n * Get the corroboration rules for an action type.\n * Useful for displaying requirements to users.\n */\nexport function getCorroborationRules(actionType: AgentActionType): readonly CorroborationRule[] {\n return ACTION_CORROBORATION_RULES[actionType];\n}\n","/**\n * nexus-agents/security/firewall - Pipeline Engine\n *\n * HostileInputFirewall: a composition layer that orchestrates existing\n * security modules (sanitizer, trust-classifier, reputation-model,\n * audit-trail) into a configurable, source-agnostic pipeline.\n *\n * Replaces ad-hoc manual composition found in issue-triage, pr-reviewer,\n * and secure-handler with a single reusable abstraction.\n *\n * @module security/firewall/firewall-pipeline\n * (Source: Issue #826 — Reusable Hostile Input Firewall)\n */\n\nimport type { Result } from '../../core/result.js';\nimport { err, ok } from '../../core/result.js';\nimport { resolveFirewallPolicyMode, type FirewallPolicyMode } from './firewall-policy-mode.js';\nimport {\n AuditTrail,\n createAuditTrail,\n emitReputationEvent,\n emitSanitizationEvent,\n MAX_STRIPPED_ELEMENTS_PER_EVENT,\n emitTrustEvent,\n} from '../audit-trail.js';\nimport { createDurableAuditSink } from '../audit-bridge.js';\nimport { sanitizeInput } from '../input-sanitizer.js';\nimport type {\n ReputationAssessment,\n GitHubUserMetadata,\n ReputationGatingMode,\n} from '../reputation-model.js';\nimport {\n assessReputation,\n ReputationCache,\n gateWithReputation,\n resolveReputationGatingMode,\n} from '../reputation-model.js';\nimport type { ReputationGateDecision } from '../reputation-model.js';\nimport type { ClassifyResult } from '../trust-classifier.js';\nimport { classifyTrust, mapAuthorAssociation } from '../trust-classifier.js';\nimport type { SanitizedInput, TrustTier } from '../trust-types.js';\nimport { generateATL } from './agent-trust-labels.js';\nimport {\n createPassthroughClassification,\n createPassthroughSanitized,\n} from './firewall-passthrough.js';\nimport { describeGate } from './firewall-trust-reason.js';\nimport type {\n ATLData,\n FirewallConfig,\n FirewallError,\n FirewallProcessOptions,\n FirewallStages,\n SourceMetadata,\n} from './firewall-types.js';\nimport { FirewallConfigSchema } from './firewall-types.js';\nimport { checkRuleOfTwo } from '../policy-gate.js';\nimport type { Violation } from '../policy-gate.js';\nimport { validateCorroboration } from '../corroboration-validator.js';\nimport type { AgentAction, SourceCitation } from '../action-schema.js';\nimport { createLogger } from '../../core/index.js';\n\nconst logger = createLogger({ component: 'HostileInputFirewall' });\n\n// ============================================================================\n// Firewall Result\n// ============================================================================\n\n/**\n * Output of the firewall pipeline. Aggregates results from each stage.\n */\nexport interface FirewallResult {\n readonly sanitized: SanitizedInput;\n readonly trust: ClassifyResult;\n /**\n * Whether the author is on the maintainer allowlist — present ONLY when an\n * allowlist was consulted (#4992), i.e. one was supplied at construction or\n * per call. `trust.isAllowlisted` is the classifier's published always-boolean\n * field and reads `false` whether the list was empty or never supplied; this\n * field is the one to record, because absence here means \"not measured\"\n * rather than \"measured false\" — the same treatment `reputationGate` gets.\n */\n readonly isAllowlisted?: boolean;\n readonly reputation?: ReputationAssessment;\n /**\n * The tier consumers should ENFORCE on (#3106): the classifier tier\n * reconciled with the reputation assessment (demotion-only; Tier-1/allowlist\n * wins; equals `trust.trustTier` when reputation is absent). Previously the\n * reputation tier was computed but dropped — `trust.trustTier` alone left\n * reputation unenforced.\n */\n readonly effectiveTrustTier: TrustTier;\n /**\n * The reputation gating decision behind `effectiveTrustTier` (#5381).\n *\n * **Absent means the reputation stage did not run** — not \"it ran and\n * suppressed nothing\". `ReputationGateDecision.demotionSuppressed` is a\n * required boolean, so surfacing it unconditionally would report `false` for a\n * check that never happened. Since the stage defaults to off, that\n * unevaluated case is the common one.\n */\n readonly reputationGate?: ReputationGateDecision;\n readonly atl: string;\n /**\n * Rule-of-Two assessment surfaced by the `policyEnforcement` stage (#3198):\n * present (with `severity: 'block'`) when the effective tier is untrusted AND\n * the configured context has both write and secret access. The firewall is a\n * signal provider — it SURFACES this for the consumer to enforce; it does not\n * hard-block. `undefined` when the stage is disabled or the rule holds.\n */\n readonly ruleOfTwoViolation?: Violation;\n /**\n * The rollout mode this run was evaluated under (#5382). Recorded on the\n * result rather than left implicit so a consumer reading a verdict can tell\n * WHICH policy produced it — a result that does not say which rules were in\n * force cannot be audited later.\n */\n readonly policyMode: FirewallPolicyMode;\n /**\n * Whether `enforce` would have refused this input.\n *\n * This is what makes `audit` mode measurable, and it is the field that makes\n * the mode a real gate rather than a switch with two indistinguishable\n * settings: under `audit` the answer is computed and reported while the input\n * is still allowed through, so an operator can size the impact of flipping to\n * `enforce` before flipping it.\n *\n * Always `false` under `enforce`, because an input that would be refused IS\n * refused — it comes back as a `POLICY_REFUSED` error, not a result.\n */\n readonly wouldRefuse: boolean;\n readonly auditEvents: readonly { readonly id: string; readonly type: string }[];\n /**\n * Whether a durable `AuditLogger` was configured for this instance (#4992\n * review). `configured` means this run's events were HANDED to that logger;\n * delivery to the hash chain is subject to the logger's own severity filter\n * (trust events are `info`), its bounded queue and its timed, fail-loud\n * flush, and is NOT confirmed per call — the write is queued. `none` means\n * the events exist only in the in-memory trail, which the next `process()`\n * call clears. This is a construction-time fact, not a per-call outcome.\n */\n readonly auditSink: 'configured' | 'none';\n readonly durationMs: number;\n}\n\n/**\n * Outcome of {@link HostileInputFirewall.validateAction} (#5382).\n *\n * A discriminated union rather than a struct with optional fields, deliberately:\n * a caller cannot read `satisfied` without first narrowing on `evaluated`, so\n * \"the stage did not run\" is structurally impossible to misread as \"the stage\n * ran and passed\". `stages.corroboration` defaults to `false`, which makes the\n * unevaluated branch the COMMON case — exactly where a silent `satisfied: true`\n * would do the most damage.\n */\nexport type ActionValidation =\n | {\n readonly evaluated: false;\n /** Why no verdict exists. Absence is attributable, not anonymous. */\n readonly reason: 'corroboration-stage-disabled';\n readonly policyMode: FirewallPolicyMode;\n }\n | {\n readonly evaluated: true;\n readonly satisfied: boolean;\n /** Unmet corroboration requirements; empty when satisfied. */\n readonly missing: readonly string[];\n readonly corroboratingSources: readonly SourceCitation[];\n readonly policyMode: FirewallPolicyMode;\n /** Whether `enforce` would have refused this action (see FirewallResult). */\n readonly wouldRefuse: boolean;\n };\n\n// ============================================================================\n// HostileInputFirewall\n// ============================================================================\n\n/**\n * Orchestrates existing security modules into a configurable pipeline.\n * Each stage is independently toggleable via config.stages.\n */\nexport class HostileInputFirewall {\n private readonly stages: FirewallStages;\n /**\n * Construction-time allowlist, or `undefined` when none was supplied (#4992).\n * The Zod default of `[]` is deliberately NOT taken here: an absent list and\n * an empty list must stay distinguishable so `isAllowlisted` can be omitted\n * rather than recorded as `false` when nothing was consulted.\n */\n private readonly allowlisted: readonly string[] | undefined;\n private readonly maxInputLength: number;\n private readonly adapter: FirewallConfig['adapter'];\n private readonly reputationCache: ReputationCache;\n private readonly auditTrail: AuditTrail;\n private readonly context: { readonly hasWriteAccess: boolean; readonly hasSecretAccess: boolean };\n /** #5382 rollout gate; resolved once at construction, not per call. */\n private readonly policyMode: FirewallPolicyMode;\n /** #5381 reputation-demotion gate; a DIFFERENT knob from `policyMode`. */\n private readonly reputationGatingMode: ReputationGatingMode;\n /** #5405 seam: how a reputation assessment is obtained. */\n private readonly assessReputationFn: (metadata: GitHubUserMetadata) => ReputationAssessment;\n /** #4992: whether the sanitizer's content tier downgrades the classifier tier. */\n private readonly contentDowngrade: boolean;\n /** #4992 review: whether a durable logger was configured (not per-call delivery). */\n private readonly auditSink: 'configured' | 'none';\n\n constructor(config: FirewallConfig) {\n const validated = FirewallConfigSchema.parse({\n stages: config.stages,\n allowlistedMaintainers: config.allowlistedMaintainers,\n maxInputLength: config.maxInputLength,\n context: config.context,\n });\n this.stages = validated.stages;\n this.allowlisted =\n config.allowlistedMaintainers !== undefined ? validated.allowlistedMaintainers : undefined;\n this.maxInputLength = validated.maxInputLength;\n this.adapter = config.adapter;\n this.reputationCache = new ReputationCache();\n // Mirror security decisions to the durable, hash-chained audit log when a\n // logger is provided; otherwise stay in-memory only (#3291).\n this.auditTrail = createAuditTrail(\n config.auditLogger !== undefined ? createDurableAuditSink(config.auditLogger) : undefined\n );\n this.auditSink = config.auditLogger !== undefined ? 'configured' : 'none';\n this.context = validated.context;\n // Explicit config wins; otherwise the environment; otherwise `off`. Resolved\n // once here rather than per `process()` call so a mid-run env change cannot\n // make two inputs in the same batch answer to different policies.\n this.policyMode = config.policyMode ?? resolveFirewallPolicyMode(config.env ?? process.env);\n // Same resolve-once discipline, but a SEPARATE knob: `NEXUS_REPUTATION_GATING`,\n // defaulting to `enforce`. Production reads this one, so the firewall reading\n // it too is what makes the two compositions agree under one configuration.\n this.reputationGatingMode =\n config.reputationGatingMode ?? resolveReputationGatingMode(config.env ?? process.env);\n this.assessReputationFn =\n config.reputationAssessor ?? ((metadata) => assessReputation(metadata, this.reputationCache));\n this.contentDowngrade = config.contentDowngrade ?? true;\n }\n\n /**\n * Processes untrusted input through the firewall pipeline.\n * Returns a structured FirewallResult or a typed FirewallError.\n *\n * `options` carries the per-call facts (#4992): the repository's maintainer\n * allowlist and the caller's access posture. Each replaces its\n * construction-time counterpart for this call only, so one shared instance\n * never holds a repository's allowlist or a caller's posture process-wide.\n */\n process(input: unknown, options?: FirewallProcessOptions): Result<FirewallResult, FirewallError> {\n const start = Date.now();\n this.auditTrail.clear();\n const allowlist = options?.allowlistedMaintainers ?? this.allowlisted;\n const context = options?.context ?? this.context;\n\n // Stage 1: Extract metadata via adapter\n const metaResult = this.runExtraction(input);\n if (!metaResult.ok) return metaResult;\n const meta = metaResult.value;\n\n // Stage 2: Sanitize input\n const sanitized = this.runSanitization(meta, allowlist);\n\n // Stage 3: Classify trust\n const trust = this.runClassification(meta, sanitized, allowlist);\n\n // Stage 4: Assess reputation — the caller's when supplied, else the stage.\n const reputation = this.runReputation(meta, sanitized, options?.reputation);\n\n const { tier: effectiveTrustTier, gate: reputationGate } = this.runReputationGate(\n trust.trustTier,\n reputation,\n options?.reputation !== undefined\n );\n\n // Trust event on the tier the consumer acts on, not the pre-reputation one.\n this.recordTrustDecision(meta, trust, effectiveTrustTier, reputationGate, allowlist);\n\n // Stage 5: Generate ATL (labelled with the enforced tier)\n const atl = this.buildATL(meta, effectiveTrustTier, sanitized, reputation);\n\n // Stage 6: Rule-of-Two policy enforcement (#3198). Signal only: the\n // consumer enforces — except under `enforce`, where it refuses (#5382).\n const ruleOfTwoViolation = this.runPolicyEnforcement(meta, effectiveTrustTier, context);\n const refusal = this.refuseIfEnforcing(ruleOfTwoViolation, meta, effectiveTrustTier);\n if (refusal !== undefined) return err(refusal);\n\n return ok(\n this.assembleResult({\n sanitized,\n trust,\n // Measured or absent: present only when an allowlist was consulted.\n isAllowlisted: allowlist !== undefined ? trust.isAllowlisted : undefined,\n reputation,\n effectiveTrustTier,\n reputationGate,\n atl,\n ruleOfTwoViolation,\n start,\n })\n );\n }\n\n /**\n * Evaluates the Rule of Two against the call's access posture and surfaces\n * the violation (previously the `policyEnforcement` stage was declared but\n * never read — #3198). Returns `undefined` when the stage is disabled or the\n * rule holds.\n */\n private runPolicyEnforcement(\n meta: SourceMetadata,\n effectiveTrustTier: TrustTier,\n context: { readonly hasWriteAccess: boolean; readonly hasSecretAccess: boolean }\n ): Violation | undefined {\n if (!this.stages.policyEnforcement) return undefined;\n const violation = checkRuleOfTwo({\n inputTrustTier: effectiveTrustTier,\n hasWriteAccess: context.hasWriteAccess,\n hasSecretAccess: context.hasSecretAccess,\n });\n if (violation !== undefined) {\n logger.warn('Firewall surfaced a Rule-of-Two violation', {\n user: meta.username,\n effectiveTrustTier,\n rule: violation.rule,\n });\n }\n return violation;\n }\n\n /**\n * Builds the successful result. Optional fields are spread in only when they\n * were evaluated, so absence keeps meaning \"not measured\" (see the field\n * docs on {@link FirewallResult}).\n */\n private assembleResult(parts: {\n readonly sanitized: SanitizedInput;\n readonly trust: ClassifyResult;\n readonly isAllowlisted: boolean | undefined;\n readonly reputation: ReputationAssessment | undefined;\n readonly effectiveTrustTier: TrustTier;\n readonly reputationGate: ReputationGateDecision | undefined;\n readonly atl: string;\n readonly ruleOfTwoViolation: Violation | undefined;\n readonly start: number;\n }): FirewallResult {\n const { isAllowlisted, reputation, reputationGate, ruleOfTwoViolation } = parts;\n const blocking = ruleOfTwoViolation?.severity === 'block';\n return {\n sanitized: parts.sanitized,\n trust: parts.trust,\n ...(isAllowlisted !== undefined ? { isAllowlisted } : {}),\n ...(reputation !== undefined ? { reputation } : {}),\n effectiveTrustTier: parts.effectiveTrustTier,\n ...(reputationGate !== undefined ? { reputationGate } : {}),\n atl: parts.atl,\n ...(ruleOfTwoViolation !== undefined ? { ruleOfTwoViolation } : {}),\n policyMode: this.policyMode,\n // Reached only when we did NOT refuse, so this is \"audit mode saw\n // something enforce would have stopped\". Under `off` it stays false: the\n // signal is already on `ruleOfTwoViolation`, and reporting a would-be\n // refusal for a mode that has not opted in would overstate the gate.\n wouldRefuse: blocking && this.policyMode === 'audit',\n auditEvents: this.auditTrail.query().map((e) => ({ id: e.id, type: e.type })),\n auditSink: this.auditSink,\n durationMs: Date.now() - parts.start,\n };\n }\n\n /**\n * The fail-closed half of the #5382 gate: under `enforce`, a blocking policy\n * violation refuses the input instead of riding along as a signal on an\n * `ok()` result that a caller checking only `result.ok` walks straight past.\n *\n * This gates the RESPONSE to a violation, never its detection. It cannot\n * manufacture a refusal where the `policyEnforcement` stage never ran, and it\n * cannot refuse a non-blocking (`warn`) violation — so `enforce` is not a\n * kill switch, and an allowlisted maintainer stays served.\n *\n * @returns the refusal, or `undefined` when the input passes or the mode has\n * not opted in.\n */\n private refuseIfEnforcing(\n violation: Violation | undefined,\n meta: SourceMetadata,\n effectiveTrustTier: TrustTier\n ): FirewallError | undefined {\n if (violation?.severity !== 'block' || this.policyMode !== 'enforce') return undefined;\n logger.warn('Firewall REFUSED input under enforce mode', {\n user: meta.username,\n effectiveTrustTier,\n rule: violation.rule,\n });\n return {\n code: 'POLICY_REFUSED',\n message: `Refused by firewall policy: ${violation.rule} — ${violation.message}`,\n stage: 'policy',\n };\n }\n\n /**\n * Validates corroboration for a decided action (#5382).\n *\n * Separate from {@link process} because the two operate at different points\n * in the lifecycle, which is the real shape of the divergence epic #5281\n * found: `process()` is INPUT-shaped — it sanitizes, classifies and labels\n * untrusted content — while corroboration is ACTION-shaped, asking whether a\n * decision the consumer has now reached is backed by sources of sufficient\n * tier. There is no `AgentAction` in scope during `process()`, so the\n * `stages.corroboration` flag could never have been wired there; this is the\n * entry point that makes it readable.\n *\n * It is also the shape #5383 needs: production validates corroboration per\n * action (`issue-triage.ts:391`), so those callers cannot migrate onto the\n * firewall unless it offers a per-action surface.\n *\n * Returns `evaluated: false` when the stage is disabled — never a satisfied\n * verdict for a check that did not run. Under `enforce` an unsatisfied action\n * is refused with `POLICY_REFUSED`; under `audit` the would-be refusal is\n * reported via `wouldRefuse` and the action is allowed through.\n */\n validateAction(action: AgentAction): Result<ActionValidation, FirewallError> {\n if (!this.stages.corroboration) {\n return ok({\n evaluated: false,\n reason: 'corroboration-stage-disabled',\n policyMode: this.policyMode,\n });\n }\n\n const corroboration = validateCorroboration(action);\n\n if (!corroboration.satisfied && this.policyMode === 'enforce') {\n logger.warn('Firewall REFUSED an uncorroborated action under enforce mode', {\n actionType: corroboration.actionType,\n missing: corroboration.missing,\n });\n return err({\n code: 'POLICY_REFUSED',\n message:\n `Refused by firewall policy: ${corroboration.actionType} lacks required ` +\n `corroboration — ${corroboration.missing.join('; ')}`,\n stage: 'corroboration',\n });\n }\n\n return ok({\n evaluated: true,\n satisfied: corroboration.satisfied,\n missing: corroboration.missing,\n corroboratingSources: corroboration.corroboratingSources,\n policyMode: this.policyMode,\n wouldRefuse: !corroboration.satisfied && this.policyMode === 'audit',\n });\n }\n\n /** Returns the internal audit trail for inspection. */\n getAuditTrail(): AuditTrail {\n return this.auditTrail;\n }\n\n // ==========================================================================\n // Pipeline Stages (private)\n // ==========================================================================\n\n private runExtraction(input: unknown): Result<SourceMetadata, FirewallError> {\n try {\n const meta = this.adapter.extractMetadata(input);\n return ok(meta);\n } catch (cause: unknown) {\n const message = cause instanceof Error ? cause.message : String(cause);\n return err({\n code: 'EXTRACTION_FAILED',\n message: `Adapter extraction failed: ${message}`,\n stage: 'extraction',\n });\n }\n }\n\n private runSanitization(\n meta: SourceMetadata,\n allowlist: readonly string[] | undefined\n ): SanitizedInput {\n if (!this.stages.sanitization) {\n return createPassthroughSanitized(meta);\n }\n\n const result = sanitizeInput(\n meta.content,\n mapAuthorAssociation(meta.authorAssociation),\n meta.username,\n {\n allowlistedMaintainers: [...(allowlist ?? [])],\n maxInputLength: this.maxInputLength,\n }\n );\n\n if (this.stages.audit) {\n emitSanitizationEvent(this.auditTrail, {\n source: meta.sourceType,\n wasModified: result.wasModified,\n ...(result.truncated !== undefined ? { truncated: result.truncated } : {}),\n strippedCount: result.strippedElements.length,\n injectionFlagCount: result.injectionFlags.length,\n strippedElements: result.strippedElements\n .slice(0, MAX_STRIPPED_ELEMENTS_PER_EVENT)\n .map((e) => ({ tag: e.tag, reason: e.reason })),\n });\n }\n\n return result;\n }\n\n private runClassification(\n meta: SourceMetadata,\n sanitized: SanitizedInput,\n allowlist: readonly string[] | undefined\n ): ClassifyResult {\n if (!this.stages.trustClassification) {\n return createPassthroughClassification(meta);\n }\n\n return classifyTrust({\n username: meta.username,\n authorAssociation: meta.authorAssociation,\n // #4992: `contentDowngrade: false` withholds the sanitizer's content tier\n // from the classifier; the flags themselves were still measured above.\n ...(this.contentDowngrade && sanitized.contentTierMeasured !== false\n ? { sanitizedInput: sanitized }\n : {}),\n config: {\n allowlistedMaintainers: [...(allowlist ?? [])],\n },\n });\n }\n\n /**\n * Emits the trust audit event for the tier the consumer acts on — after the\n * reputation gate, so the record cannot describe a tier nobody enforced\n * (#4992 review). A demotion is named in `reason`.\n */\n private recordTrustDecision(\n meta: SourceMetadata,\n trust: ClassifyResult,\n effectiveTrustTier: TrustTier,\n gate: ReputationGateDecision | undefined,\n allowlist: readonly string[] | undefined\n ): void {\n if (!this.stages.audit) return;\n const demoted = effectiveTrustTier !== trust.trustTier;\n emitTrustEvent(this.auditTrail, {\n username: meta.username,\n assignedTier: effectiveTrustTier,\n userRole: trust.userRole,\n // Recorded only when an allowlist was consulted (#4992).\n ...(allowlist !== undefined ? { isAllowlisted: trust.isAllowlisted } : {}),\n wasDowngraded: trust.wasDowngraded || demoted,\n reason: describeGate(trust.reason, demoted, gate),\n });\n }\n\n /**\n * Reconcile the classifier tier with reputation, under the rollout mode\n * production honours (#3106 reconciliation, #5381 gating).\n *\n * #3106's reconciliation is demotion-only; Tier-1/allowlist wins; it equals\n * the classifier tier when reputation is absent. #5381 puts it behind\n * `NEXUS_REPUTATION_GATING`, so `audit` is audit-only here too — previously\n * the firewall enforced unconditionally while production suppressed, on\n * identical configuration.\n *\n * Returns no gate at all when the stage is off, so absence in the result\n * means \"not evaluated\" rather than \"evaluated, nothing suppressed\" —\n * `demotionSuppressed` is a required boolean and would otherwise report\n * `false` for a check that never ran.\n */\n private runReputationGate(\n classifierTier: TrustTier,\n reputation: ReputationAssessment | undefined,\n callerSupplied: boolean\n ): { readonly tier: TrustTier; readonly gate?: ReputationGateDecision } {\n // A caller-supplied measurement runs the gate even when the instance's own\n // reputation stage is off (#4992 review): the caller measured, so record it.\n if (!this.stages.reputationAssessment && !callerSupplied) return { tier: classifierTier };\n const gate = gateWithReputation(classifierTier, reputation, this.reputationGatingMode);\n return { tier: gate.enforcedTier, gate };\n }\n\n private runReputation(\n meta: SourceMetadata,\n sanitized: SanitizedInput,\n supplied: { readonly assessment: ReputationAssessment | undefined } | undefined\n ): ReputationAssessment | undefined {\n if (supplied !== undefined) {\n // The caller's measurement replaces the instance's stage for this call;\n // `undefined` means the caller measured nothing, so nothing is emitted.\n if (supplied.assessment !== undefined && this.stages.audit) {\n emitReputationEvent(this.auditTrail, {\n username: meta.username,\n reputationScore: supplied.assessment.reputationScore,\n isSuspicious: supplied.assessment.isSuspicious,\n effectiveTier: supplied.assessment.effectiveTrustTier,\n signalCount: supplied.assessment.suspiciousSignals.length,\n });\n }\n return supplied.assessment;\n }\n if (!this.stages.reputationAssessment) return undefined;\n\n // #3106: only supply what the firewall actually knows from the event —\n // author association + injection flags. Account-age / contribution /\n // recent-comment data is NOT available here (it's fetched at the wiring\n // layer in a later phase), so it is OMITTED, not fabricated. The engine\n // skips the account/activity signals when their data is absent; until the\n // fetch lands, the firewall's reputation reflects injection + authority\n // signals only — honest rather than always-benign.\n const metadata: GitHubUserMetadata = {\n username: meta.username,\n authorAssociation: meta.authorAssociation.toUpperCase(),\n injectionFlags: sanitized.injectionFlags,\n };\n\n const result = this.assessReputationFn(metadata);\n\n if (this.stages.audit) {\n emitReputationEvent(this.auditTrail, {\n username: meta.username,\n reputationScore: result.reputationScore,\n isSuspicious: result.isSuspicious,\n effectiveTier: result.effectiveTrustTier,\n signalCount: result.suspiciousSignals.length,\n });\n }\n\n return result;\n }\n\n private buildATL(\n meta: SourceMetadata,\n effectiveTrustTier: TrustTier,\n sanitized: SanitizedInput,\n reputation?: ReputationAssessment\n ): string {\n const data: ATLData = {\n tier: effectiveTrustTier,\n source: meta.sourceType,\n user: meta.username,\n sanitized: sanitized.wasModified,\n ...(reputation !== undefined ? { rep: reputation.reputationScore / 100 } : {}),\n };\n return generateATL(data);\n }\n}\n","/**\n * nexus-agents/security/firewall - GitHub Adapter\n *\n * ISourceAdapter implementation for GitHub issues, PRs, and comments.\n * Extracts normalized metadata from GitHub API payloads.\n *\n * @module security/firewall/github-adapter\n * (Source: Issue #826 — Reusable Hostile Input Firewall)\n */\n\nimport { z } from 'zod';\n\nimport { mapAuthorAssociation } from '../trust-classifier.js';\nimport type { ISourceAdapter, SourceMetadata } from './firewall-types.js';\n\n// ============================================================================\n// GitHub Input Types\n// ============================================================================\n\n/**\n * Discriminated union of GitHub input types the adapter can process.\n */\nconst GitHubIssueSchema = z.object({\n type: z.literal('issue'),\n username: z.string().min(1),\n authorAssociation: z.string().min(1),\n title: z.string().default(''),\n body: z.string().default(''),\n});\n\nconst GitHubCommentSchema = z.object({\n type: z.literal('comment'),\n username: z.string().min(1),\n authorAssociation: z.string().min(1),\n body: z.string().default(''),\n});\n\nconst GitHubPRSchema = z.object({\n type: z.literal('pull_request'),\n username: z.string().min(1),\n authorAssociation: z.string().min(1),\n title: z.string().default(''),\n body: z.string().default(''),\n});\n\nexport const GitHubInputSchema = z.discriminatedUnion('type', [\n GitHubIssueSchema,\n GitHubCommentSchema,\n GitHubPRSchema,\n]);\nexport type GitHubInput = z.infer<typeof GitHubInputSchema>;\n\n// ============================================================================\n// GitHub Adapter\n// ============================================================================\n\n/** Maps input type to source type string. */\nfunction toSourceType(type: GitHubInput['type']): string {\n const mapping: Record<GitHubInput['type'], string> = {\n issue: 'github-issue',\n comment: 'github-comment',\n pull_request: 'github-pr',\n };\n return mapping[type];\n}\n\n/** Extracts content from a GitHub input. */\nfunction extractContent(input: GitHubInput): string {\n if (input.type === 'comment') return input.body;\n const title = input.title;\n const body = input.body;\n return title && body ? `${title}\\n\\n${body}` : title || body;\n}\n\n/**\n * Creates a GitHub source adapter.\n * Validates input with Zod and maps GitHub API fields to SourceMetadata.\n */\nexport function createGitHubAdapter(): ISourceAdapter {\n return {\n platform: 'github',\n extractMetadata(input: unknown): SourceMetadata {\n const result = GitHubInputSchema.safeParse(input);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => `${i.path.join('.')}: ${i.message}`)\n .join('; ');\n throw new Error(`GitHub input validation failed: ${issues}`);\n }\n const parsed = result.data;\n const role = mapAuthorAssociation(parsed.authorAssociation);\n return {\n username: parsed.username,\n authorAssociation: role,\n content: extractContent(parsed),\n sourceType: toSourceType(parsed.type),\n };\n },\n };\n}\n","/**\n * nexus-agents/dogfooding - Untrusted-input firewall (shared instance)\n *\n * The one `HostileInputFirewall` the live untrusted-input paths route through\n * (#4992): `issue-triage` and `pr-reviewer`. Before this module both called\n * `classifyTrust()` directly, which meant no trust classification on any live\n * path reached the security audit trail, and the maintainer allowlist the\n * classifier reads from `config` was never supplied — `isAllowlisted: false`\n * was recorded on every result without ever being measured.\n *\n * Division of labour, ratified by the #4992 panel:\n *\n * - The firewall is INPUT-shaped: it sanitizes, classifies, evaluates the\n * Rule of Two against the caller's access posture, and records the trust\n * event. Under the default `NEXUS_FIREWALL_POLICY=off` it is signal-only.\n * - The callers keep their own `evaluatePolicy` (Rule of Two per action) and\n * measure reputation themselves with metadata the firewall cannot see\n * (account age, comment history). They pass that measurement per call, and\n * the firewall runs the ONE reputation gate both sides act on — so its\n * Rule-of-Two check, `wouldRefuse` count and trust audit event use the same\n * enforced tier the caller's policy gate uses.\n * - Facts that vary per call — the caller's access posture and, when a source\n * for one exists, the repository's maintainer allowlist — are passed per\n * call via `FirewallProcessOptions`, never held on the shared instance. No\n * allowlist source exists today (no config field, no env var), so the\n * callers pass none and `isAllowlisted` stays absent on their results.\n *\n * @module dogfooding/untrusted-input-firewall\n */\n\nimport type { Result } from '../core/index.js';\nimport { ok, err, createLogger } from '../core/index.js';\nimport type { IAuditLogger } from '../audit/audit-types.js';\nimport { HostileInputFirewall } from '../security/firewall/firewall-pipeline.js';\nimport type { FirewallResult } from '../security/firewall/firewall-pipeline.js';\nimport type { FirewallProcessOptions } from '../security/firewall/firewall-types.js';\nimport { createGitHubAdapter } from '../security/firewall/github-adapter.js';\nimport type { GitHubInput } from '../security/firewall/github-adapter.js';\n\nconst logger = createLogger({ component: 'UntrustedInputFirewall' });\n\nlet singleton: HostileInputFirewall | undefined;\n/** The process's durable audit logger, when the bootstrap has one (#4992 review). */\nlet configuredAuditLogger: IAuditLogger | undefined;\n\n/**\n * Supplies the process-wide durable audit logger to the shared firewall.\n *\n * The MCP server creates exactly one `AuditLogger` at startup\n * (`initializeAuditLogger`, gated on `security.audit.enabled`) and threads it\n * by DI; there is no global accessor. `initV2PipelineSubsystems` calls this\n * with that logger so trust events on the live paths reach the hash-chained\n * log instead of an in-memory buffer the next call clears. Without it — the\n * CLI review path, or audit disabled — the firewall reports `auditSink: 'none'`\n * and claims no emission.\n *\n * Replaces the cached instance so the sink cannot be missed by an instance\n * built before bootstrap reached this point.\n */\nexport function configureUntrustedInputFirewall(deps: { auditLogger?: IAuditLogger }): void {\n configuredAuditLogger = deps.auditLogger;\n singleton = undefined;\n}\n\n/**\n * The process-wide firewall, constructed on first use.\n *\n * `contentDowngrade: false` is what keeps the default mode pass-through: the\n * firewall's own classifier is content-aware (injection-bearing content from a\n * Tier-2 author classifies as Tier 4), while the production paths classify by\n * role and route content signals through reputation gating, which has its own\n * rollout knob (`NEXUS_REPUTATION_GATING`). The injection flags are still\n * measured and recorded by the sanitization stage; only the tier downgrade is\n * withheld so the recorded `trustTier` is unchanged under `off`.\n *\n * The reputation stage stays off: the callers assess reputation themselves\n * with metadata the firewall never sees. `policyMode` is resolved from\n * `NEXUS_FIREWALL_POLICY` once, at construction.\n */\nexport function getUntrustedInputFirewall(): HostileInputFirewall {\n singleton ??= new HostileInputFirewall({\n adapter: createGitHubAdapter(),\n contentDowngrade: false,\n ...(configuredAuditLogger !== undefined ? { auditLogger: configuredAuditLogger } : {}),\n });\n return singleton;\n}\n\n// @export-no-consumer-yet — see #4992. Test-only seam: drops or replaces the\n// cached instance so a test can re-read the env or observe the audit trail.\nexport function _setUntrustedInputFirewallForTests(\n firewall: HostileInputFirewall | undefined\n): void {\n singleton = firewall;\n}\n\n/**\n * Runs one untrusted GitHub payload through the shared firewall.\n *\n * Maps the firewall's typed error into the `Result<_, Error>` shape the\n * callers already return, failing closed on every code: an unparseable payload\n * (`EXTRACTION_FAILED`) is as much a reason not to proceed as a policy\n * refusal (`POLICY_REFUSED`, only reachable under `enforce`). Under `audit` a\n * would-be refusal is logged and the result is returned unchanged — that log\n * line is the telemetry an operator sizes the flip to `enforce` on.\n */\nexport function runUntrustedInputFirewall(\n input: GitHubInput,\n options: FirewallProcessOptions\n): Result<FirewallResult, Error> {\n const result = getUntrustedInputFirewall().process(input, options);\n if (!result.ok) {\n const { code, stage, message } = result.error;\n logger.warn('Untrusted-input firewall rejected the input', {\n user: input.username,\n sourceType: input.type,\n code,\n stage,\n });\n return err(new Error(`Untrusted-input firewall ${code} at stage ${stage}: ${message}`));\n }\n if (result.value.wouldRefuse) {\n logger.warn('Untrusted-input firewall would refuse under enforce (audit mode)', {\n user: input.username,\n sourceType: input.type,\n trustTier: result.value.effectiveTrustTier,\n rule: result.value.ruleOfTwoViolation?.rule,\n });\n }\n return ok(result.value);\n}\n","/**\n * nexus-agents/scm - URL parsers\n *\n * Pure parsers for GitHub PR and issue URLs. Lifted from\n * `dogfooding/github-client.ts` as part of the #2553 consolidation —\n * URL parsing has no transport-layer dependency, so it belongs in the\n * canonical SCM module alongside the rest of the GitHub surface.\n *\n * @module scm/url-parsers\n */\n\nimport type { Result } from '../core/index.js';\nimport { ok, err } from '../core/index.js';\n\n/**\n * Parses a PR URL into owner, repo, and number. Accepts:\n *\n * - `https://github.com/owner/repo/pull/123`\n * - `https://www.github.com/owner/repo/pull/123`\n * - `owner/repo#123`\n * - `owner/repo/pull/123`\n */\nexport function parsePRUrl(url: string): Result<\n {\n owner: string;\n repo: string;\n prNumber: number;\n },\n Error\n> {\n const httpPattern = /github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/(\\d+)/;\n const shortPattern = /^([^/]+)\\/([^/#]+)(?:#|\\/pull\\/)(\\d+)$/;\n\n const match = httpPattern.exec(url) ?? shortPattern.exec(url);\n\n if (match === null) {\n return err(new Error(`Invalid PR URL format: ${url}`));\n }\n\n const owner = match[1];\n const repo = match[2];\n const numberStr = match[3];\n\n if (owner === undefined || repo === undefined || numberStr === undefined) {\n return err(new Error(`Invalid PR URL format: ${url}`));\n }\n\n const prNumber = parseInt(numberStr, 10);\n\n if (isNaN(prNumber)) {\n return err(new Error(`Invalid PR URL format: ${url}`));\n }\n\n return ok({ owner, repo, prNumber });\n}\n\n/**\n * Parses an issue URL into owner, repo, and number. Accepts:\n *\n * - `https://github.com/owner/repo/issues/123`\n * - `owner/repo#123`\n */\nexport function parseIssueUrl(url: string): Result<\n {\n owner: string;\n repo: string;\n issueNumber: number;\n },\n Error\n> {\n const httpPattern = /github\\.com\\/([^/]+)\\/([^/]+)\\/issues\\/(\\d+)/;\n const shortPattern = /^([^/]+)\\/([^/#]+)#(\\d+)$/;\n\n const match = httpPattern.exec(url) ?? shortPattern.exec(url);\n\n if (match === null) {\n return err(new Error(`Invalid issue URL format: ${url}`));\n }\n\n const owner = match[1];\n const repo = match[2];\n const numberStr = match[3];\n\n if (owner === undefined || repo === undefined || numberStr === undefined) {\n return err(new Error(`Invalid issue URL format: ${url}`));\n }\n\n const issueNumber = parseInt(numberStr, 10);\n\n if (isNaN(issueNumber)) {\n return err(new Error(`Invalid issue URL format: ${url}`));\n }\n\n return ok({ owner, repo, issueNumber });\n}\n","/**\n * nexus-agents/scm - GitHub Provider Trait Implementations\n *\n * Implements IScmReviewer and IScmUserInfo trait interfaces for GitHub.\n * Uses `gh api` for REST API access to get detailed data.\n *\n * @module scm/github-provider-traits\n * (Source: Issue #1136 — Centralized SCM Provider Module)\n */\n\nimport type { Result } from '../core/index.js';\nimport { ok, err, createLogger } from '../core/index.js';\nimport type {\n IScmReviewer,\n IScmUserInfo,\n ScmPullRequestDetail,\n ScmIssueDetail,\n ScmCommentDetail,\n ScmReviewDecision,\n ScmUserMetadata,\n ScmFileChange,\n} from './types.js';\nimport { ScmError } from './types.js';\nimport { GitHubProvider } from './github-provider.js';\nimport { CLI_SUBPROCESS_TIMEOUTS } from '../config/timeouts.js';\n\nconst logger = createLogger({ component: 'GitHubProviderTraits' });\n\n// ============================================================================\n// gh API JSON types (internal)\n// ============================================================================\n\ninterface GhApiPrJson {\n number: number;\n title: string;\n body: string | null;\n html_url: string;\n user: { login: string };\n author_association: string;\n base: { ref: string };\n head: { ref: string; sha: string };\n draft: boolean;\n labels: Array<{ name: string }>;\n additions: number;\n deletions: number;\n}\n\ninterface GhApiFileJson {\n filename: string;\n status: string;\n additions: number;\n deletions: number;\n patch?: string;\n previous_filename?: string;\n}\n\ninterface GhApiIssueJson {\n number: number;\n title: string;\n body: string | null;\n user: { login: string };\n author_association: string;\n state: string;\n html_url: string;\n labels: Array<{ name: string }>;\n created_at: string;\n}\n\ninterface GhApiCommentJson {\n id: number;\n body: string;\n user: { login: string };\n author_association: string;\n created_at: string;\n}\n\ninterface GhApiUserJson {\n login: string;\n name: string | null;\n company: string | null;\n followers: number;\n following: number;\n public_repos: number;\n created_at: string;\n}\n\n// ============================================================================\n// gh API executor\n// ============================================================================\n\n/**\n * Resolves a GitHub token from env vars or `gh auth token`.\n * Caches the result to avoid repeated subprocess calls.\n */\nlet cachedGhToken: string | undefined | null = null;\nlet ghTokenPromise: Promise<string | undefined> | undefined;\n\n/** Reset the cached token (for testing). */\nexport function resetGhTokenCache(): void {\n cachedGhToken = null;\n ghTokenPromise = undefined;\n}\n\nasync function resolveGhToken(): Promise<string | undefined> {\n if (cachedGhToken !== null) return cachedGhToken;\n\n ghTokenPromise ??= resolveGhTokenImpl().finally(() => {\n ghTokenPromise = undefined;\n });\n return ghTokenPromise;\n}\n\nasync function resolveGhTokenImpl(): Promise<string | undefined> {\n const envToken = process.env['GITHUB_TOKEN'] ?? process.env['GH_TOKEN'];\n if (envToken !== undefined && envToken.length > 0) {\n cachedGhToken = envToken;\n return envToken;\n }\n\n try {\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const exec = promisify(execFile);\n const { stdout } = await exec('gh', ['auth', 'token'], { timeout: 5_000 });\n const token = stdout.trim();\n cachedGhToken = token.length > 0 ? token : undefined;\n } catch {\n cachedGhToken = undefined;\n }\n return cachedGhToken;\n}\n\nasync function execGhApi(endpoint: string, method?: string): Promise<Result<string, ScmError>> {\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const exec = promisify(execFile);\n\n const args = ['api', endpoint];\n if (method !== undefined) args.push('--method', method);\n\n // Inject token into subprocess environment to prevent keyring access failures\n const token = await resolveGhToken();\n const env = token !== undefined ? { ...process.env, GH_TOKEN: token } : undefined;\n\n try {\n const { stdout } = await exec('gh', args, {\n maxBuffer: 10 * 1024 * 1024,\n timeout: CLI_SUBPROCESS_TIMEOUTS.ghCommandMs,\n ...(env !== undefined ? { env } : {}),\n });\n return ok(stdout.trim());\n } catch (error) {\n const execError = error as { message: string; stderr?: string };\n return err(\n new ScmError(`gh api failed: ${execError.message}`, 'github', undefined, {\n endpoint,\n stderr: execError.stderr,\n })\n );\n }\n}\n\n// ============================================================================\n// Mappers\n// ============================================================================\n\nfunction mapFileChange(raw: GhApiFileJson): ScmFileChange {\n const statusMap: Record<string, ScmFileChange['status']> = {\n added: 'added',\n removed: 'removed',\n modified: 'modified',\n renamed: 'renamed',\n copied: 'copied',\n changed: 'changed',\n unchanged: 'unchanged',\n };\n const status = statusMap[raw.status] ?? 'unknown';\n return {\n filename: raw.filename,\n status,\n ...(status === 'unknown' ? { rawStatus: raw.status } : {}),\n additions: raw.additions,\n deletions: raw.deletions,\n ...(raw.patch !== undefined ? { patch: raw.patch } : {}),\n ...(raw.previous_filename !== undefined ? { previousFilename: raw.previous_filename } : {}),\n };\n}\n\n// ============================================================================\n// GitHubReviewer — implements IScmReviewer\n// ============================================================================\n\n/**\n * GitHub-specific reviewer that adds PR detail and review capabilities\n * to a GitHubProvider. Implements IScmReviewer trait.\n *\n * @example\n * ```typescript\n * const provider = createGitHubProvider('owner/repo');\n * const reviewer = new GitHubReviewer(provider);\n * const detail = await reviewer.getPullRequestDetail(42);\n * ```\n */\nexport class GitHubReviewer implements IScmReviewer {\n constructor(private readonly provider: GitHubProvider) {}\n\n async getPullRequestDetail(prNumber: number): Promise<Result<ScmPullRequestDetail, ScmError>> {\n const repo = this.provider.repo;\n logger.debug('Getting PR detail', { repo, prNumber });\n\n const prResult = await execGhApi(`repos/${repo}/pulls/${String(prNumber)}`);\n if (!prResult.ok) return prResult;\n\n const filesResult = await execGhApi(`repos/${repo}/pulls/${String(prNumber)}/files`);\n if (!filesResult.ok) return filesResult;\n\n try {\n const pr = JSON.parse(prResult.value) as GhApiPrJson;\n const files = JSON.parse(filesResult.value) as GhApiFileJson[];\n\n return ok({\n number: pr.number,\n title: pr.title,\n body: pr.body ?? '',\n author: pr.user.login,\n base: pr.base.ref,\n head: pr.head.ref,\n url: pr.html_url,\n draft: pr.draft,\n authorAssociation: pr.author_association,\n labels: pr.labels.map((l) => l.name),\n files: files.map(mapFileChange),\n additions: pr.additions,\n deletions: pr.deletions,\n headSha: pr.head.sha,\n });\n } catch {\n return err(new ScmError('Failed to parse PR detail JSON', 'github'));\n }\n }\n\n async createReview(\n prNumber: number,\n body: string,\n decision: ScmReviewDecision\n ): Promise<Result<void, ScmError>> {\n const repo = this.provider.repo;\n const eventMap: Record<ScmReviewDecision, string> = {\n approve: 'APPROVE',\n request_changes: 'REQUEST_CHANGES',\n comment: 'COMMENT',\n };\n\n logger.info('Creating review', { repo, prNumber, decision });\n\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const exec = promisify(execFile);\n\n try {\n await exec(\n 'gh',\n [\n 'api',\n `repos/${repo}/pulls/${String(prNumber)}/reviews`,\n '--method',\n 'POST',\n '-f',\n `body=${body}`,\n '-f',\n `event=${eventMap[decision]}`,\n ],\n { maxBuffer: 10 * 1024 * 1024, timeout: CLI_SUBPROCESS_TIMEOUTS.ghCommandMs }\n );\n return ok(undefined);\n } catch (error) {\n const execError = error as { message: string };\n return err(new ScmError(`Failed to create review: ${execError.message}`, 'github'));\n }\n }\n\n async getIssueDetail(issueNumber: number): Promise<Result<ScmIssueDetail, ScmError>> {\n const repo = this.provider.repo;\n logger.debug('Getting issue detail', { repo, issueNumber });\n\n const result = await execGhApi(`repos/${repo}/issues/${String(issueNumber)}`);\n if (!result.ok) return result;\n\n try {\n const raw = JSON.parse(result.value) as GhApiIssueJson;\n return ok({\n number: raw.number,\n title: raw.title,\n body: raw.body ?? '',\n labels: raw.labels.map((l) => l.name),\n author: raw.user.login,\n createdAt: raw.created_at,\n authorAssociation: raw.author_association,\n state: raw.state,\n url: raw.html_url,\n });\n } catch {\n return err(new ScmError('Failed to parse issue detail JSON', 'github'));\n }\n }\n\n async listCommentDetails(\n issueNumber: number\n ): Promise<Result<readonly ScmCommentDetail[], ScmError>> {\n const repo = this.provider.repo;\n logger.debug('Listing comment details', { repo, issueNumber });\n\n const result = await execGhApi(`repos/${repo}/issues/${String(issueNumber)}/comments`);\n if (!result.ok) return result;\n\n try {\n const comments = JSON.parse(result.value) as GhApiCommentJson[];\n return ok(\n comments.map((c) => ({\n id: c.id,\n body: c.body,\n author: c.user.login,\n createdAt: c.created_at,\n authorAssociation: c.author_association,\n }))\n );\n } catch {\n return err(new ScmError('Failed to parse comment details JSON', 'github'));\n }\n }\n}\n\n// ============================================================================\n// GitHubUserInfo — implements IScmUserInfo\n// ============================================================================\n\n/**\n * GitHub-specific user info provider. Implements IScmUserInfo trait.\n */\nexport class GitHubUserInfo implements IScmUserInfo {\n async fetchUserMetadata(username: string): Promise<Result<ScmUserMetadata, ScmError>> {\n logger.debug('Fetching user metadata', { username });\n\n const result = await execGhApi(`users/${username}`);\n if (!result.ok) return result;\n\n try {\n const raw = JSON.parse(result.value) as GhApiUserJson;\n return ok({\n login: raw.login,\n name: raw.name,\n company: raw.company,\n followers: raw.followers,\n following: raw.following,\n publicRepos: raw.public_repos,\n createdAt: raw.created_at,\n });\n } catch {\n return err(new ScmError('Failed to parse user metadata JSON', 'github'));\n }\n }\n}\n\n// ============================================================================\n// Factory — compose provider with traits\n// ============================================================================\n\n/**\n * Creates a full-capability GitHub provider with all traits.\n *\n * Returns an object that implements IScmProvider & IScmReviewer & IScmUserInfo.\n * Consumers can narrow the type to only the traits they need.\n *\n * @example\n * ```typescript\n * const provider = createFullGitHubProvider('owner/repo');\n * // Use as ReviewCapableProvider\n * const detail = await provider.getPullRequestDetail(42);\n * // Use as IScmUserInfo\n * const user = await provider.fetchUserMetadata('octocat');\n * ```\n */\nexport function createFullGitHubProvider(\n repo: string\n): GitHubProvider & IScmReviewer & IScmUserInfo {\n const base = new GitHubProvider(repo);\n const reviewer = new GitHubReviewer(base);\n const userInfo = new GitHubUserInfo();\n\n // Compose all trait methods onto the provider\n return Object.assign(base, {\n getPullRequestDetail: reviewer.getPullRequestDetail.bind(reviewer),\n createReview: reviewer.createReview.bind(reviewer),\n getIssueDetail: reviewer.getIssueDetail.bind(reviewer),\n listCommentDetails: reviewer.listCommentDetails.bind(reviewer),\n fetchUserMetadata: userInfo.fetchUserMetadata.bind(userInfo),\n });\n}\n","/**\n * nexus-agents/dogfooding - Issue Triage Types\n *\n * Type definitions for automated GitHub issue triage using\n * the security pipeline (trust classification, corroboration,\n * reputation model).\n *\n * @module dogfooding/issue-triage-types\n * (Source: Issue #828 — Wire remaining security modules)\n */\n\nimport { z } from 'zod';\nimport type { ReputationAssessment } from '../security/reputation-model.js';\n\n// ============================================================================\n// Issue Metadata\n// ============================================================================\n\n/**\n * GitHub issue metadata from the API.\n */\nexport interface IssueMetadata {\n /** Issue number */\n readonly number: number;\n /** Issue title */\n readonly title: string;\n /** Issue body/description */\n readonly body: string;\n /** Author username */\n readonly author: string;\n /** GitHub API author_association (e.g., 'OWNER', 'COLLABORATOR', 'NONE') */\n readonly authorAssociation: string;\n /** Repository owner */\n readonly owner: string;\n /** Repository name */\n readonly repo: string;\n /** Issue URL */\n readonly url: string;\n /** Issue state ('open' | 'closed') */\n readonly state: string;\n /** Issue labels */\n readonly labels: readonly string[];\n /** Creation timestamp (ISO 8601) */\n readonly createdAt: string;\n}\n\n/**\n * GitHub issue comment from the API.\n */\nexport interface IssueComment {\n /** Comment ID */\n readonly id: number;\n /** Comment body */\n readonly body: string;\n /** Author username */\n readonly author: string;\n /** GitHub API author_association */\n readonly authorAssociation: string;\n /** Creation timestamp (ISO 8601) */\n readonly createdAt: string;\n}\n\n// ============================================================================\n// Issue Classification\n// ============================================================================\n\n/**\n * Issue category determined by keyword-based classification.\n */\nexport type IssueCategory =\n 'bug' | 'feature' | 'question' | 'documentation' | 'security' | 'performance';\n\n/**\n * Display names for issue categories.\n */\nexport const CATEGORY_DISPLAY_NAMES: Record<IssueCategory, string> = {\n bug: 'Bug Report',\n feature: 'Feature Request',\n question: 'Question',\n documentation: 'Documentation',\n security: 'Security',\n performance: 'Performance',\n};\n\n/**\n * Emoji for issue categories (GitHub markdown).\n */\nexport const CATEGORY_EMOJI: Record<IssueCategory, string> = {\n bug: ':bug:',\n feature: ':sparkles:',\n question: ':question:',\n documentation: ':books:',\n security: ':lock:',\n performance: ':zap:',\n};\n\n// ============================================================================\n// Triage Configuration\n// ============================================================================\n\n/**\n * Configuration for issue triage.\n */\nexport interface IssueTriageConfig {\n /** Whether to run in dry-run mode (no GitHub mutations). Default: true */\n readonly dryRun: boolean;\n /** GitHub token for API access */\n readonly githubToken?: string | undefined;\n /** Maximum comments to fetch per issue */\n readonly maxComments: number;\n /** Whether to use reputation model for trust assessment */\n readonly enableReputation: boolean;\n}\n\n/**\n * Default issue triage configuration.\n * Read-only by default — proposes actions without executing them.\n */\nexport const DEFAULT_ISSUE_TRIAGE_CONFIG: IssueTriageConfig = {\n dryRun: true,\n maxComments: 50,\n enableReputation: true,\n};\n\n/**\n * Zod schema for issue triage configuration.\n */\nexport const IssueTriageConfigSchema = z.object({\n dryRun: z.boolean().default(true),\n githubToken: z.string().optional(),\n maxComments: z.number().int().min(1).max(100).default(50),\n enableReputation: z.boolean().default(true),\n});\n\n// ============================================================================\n// Proposed Actions\n// ============================================================================\n\n/**\n * A proposed action from the triage pipeline.\n * All actions are validated through the security pipeline before output.\n */\nexport interface ProposedAction {\n /** Action type (matches AgentActionType from security/action-schema) */\n readonly type: string;\n /** Human-readable description of the proposed action */\n readonly description: string;\n /** Whether this action was approved by the policy gate */\n readonly policyApproved: boolean;\n /** Whether corroboration requirements were satisfied */\n readonly corroborated: boolean;\n /** Details specific to the action type */\n readonly details: Record<string, unknown>;\n}\n\n// ============================================================================\n// Trust Assessment\n// ============================================================================\n\n/**\n * Trust assessment summary for the triage result.\n */\nexport interface TrustAssessment {\n /** Author's trust tier (1-4) */\n readonly trustTier: string;\n /** Author's GitHub role */\n readonly userRole: string;\n /**\n * Whether the author is on the maintainer allowlist — present only when an\n * allowlist was consulted (#4992). Absent means \"no allowlist was consulted\",\n * which is the state on every live path today; it was previously recorded\n * as `false` without being measured.\n */\n readonly isAllowlisted?: boolean;\n /**\n * Whether a durable `AuditLogger` was configured for this process (#4992\n * review). `configured` — the trust event was handed to it; delivery to the\n * hash chain is subject to the logger's own severity filter (trust events\n * are `info`), queue and fail-loud flush, and is not confirmed per call.\n * `none` — no audit logger (audit disabled, or the CLI path), so the event\n * exists only in the firewall's in-memory trail.\n */\n readonly auditSink: 'configured' | 'none';\n /** Reputation score (0-100) if reputation model is enabled */\n readonly reputationScore?: number | undefined;\n /** Coverage of reputation dimensions; `unmeasured` is distinct from zero activity. */\n readonly coverage?: ReputationAssessment['coverage'] | undefined;\n /** Suspicious signals detected */\n readonly suspiciousSignals: readonly string[];\n /** Whether the author is flagged as suspicious */\n readonly isSuspicious: boolean;\n /**\n * Tier the policy gate ACTUALLY enforced (#3122). Equals `trustTier` under\n * `audit`/`off`; equals `reputationReconciledTier` under `enforce`. This is\n * the tier `proposedActions[].policyApproved` was decided against.\n */\n readonly enforcedTrustTier?: string | undefined;\n /**\n * Tier reputation reconciliation computed — what `enforce` mode WOULD gate on\n * (#3122). May exceed `trustTier` (more restrictive) when reputation demotes.\n * Under `audit`/`off` this is reported for telemetry but NOT enforced — read\n * `enforcedTrustTier` for the tier actually in effect.\n */\n readonly reputationReconciledTier?: string | undefined;\n /** Reputation-gating rollout mode applied: `off` | `audit` | `enforce` (#3122). */\n readonly gatingMode?: string | undefined;\n}\n\n// ============================================================================\n// Triage Result\n// ============================================================================\n\n/**\n * Complete triage result for a GitHub issue.\n */\nexport interface IssueTriageResult {\n /** Issue number */\n readonly issueNumber: number;\n /** Repository (owner/repo) */\n readonly repository: string;\n /** Proposed actions (validated through security pipeline) */\n readonly proposedActions: readonly ProposedAction[];\n /** Trust assessment of the issue author */\n readonly trustAssessment: TrustAssessment;\n /** Detected issue category */\n readonly category: IssueCategory;\n /** Category confidence (0-1) */\n readonly categoryConfidence: number;\n /** Total execution time in ms */\n readonly totalDurationMs: number;\n /** Timestamp of triage (ISO 8601) */\n readonly timestamp: string;\n}\n","/**\n * nexus-agents/dogfooding - Issue Triage Helpers\n *\n * Pure helper functions for issue classification, label extraction,\n * and result formatting. No side effects or API calls.\n *\n * @module dogfooding/issue-triage-helpers\n * (Source: Issue #828 — Wire remaining security modules)\n */\n\nimport type { CorroborationResult } from '../security/corroboration-validator.js';\nimport type { AgentAction } from '../security/action-schema.js';\nimport type { ScmCommentDetail } from '../scm/types.js';\nimport type {\n IssueCategory,\n IssueComment,\n IssueTriageResult,\n ProposedAction,\n} from './issue-triage-types.js';\nimport { CATEGORY_DISPLAY_NAMES, CATEGORY_EMOJI } from './issue-triage-types.js';\n\n// ============================================================================\n// Issue Classification\n// ============================================================================\n\n/**\n * Keyword sets for each issue category.\n * Lower-cased for case-insensitive matching.\n */\nconst CATEGORY_KEYWORDS: Record<IssueCategory, readonly string[]> = {\n bug: ['bug', 'error', 'crash', 'broken', 'fix', 'fail', 'issue', 'wrong', 'unexpected'],\n feature: ['feature', 'request', 'enhancement', 'proposal', 'add', 'support', 'implement'],\n question: ['question', 'how to', 'help', 'confused', 'explain', 'documentation'],\n documentation: ['docs', 'documentation', 'readme', 'typo', 'example', 'guide'],\n security: ['security', 'vulnerability', 'cve', 'exploit', 'injection', 'xss', 'csrf'],\n performance: ['performance', 'slow', 'memory', 'leak', 'optimize', 'latency', 'timeout'],\n};\n\n/**\n * Classifies an issue by matching keywords in the title and body.\n * Returns the category with the highest keyword match count.\n *\n * @param title - Issue title\n * @param body - Issue body\n * @returns Tuple of [category, confidence]\n */\nexport function categorizeIssue(title: string, body: string): [IssueCategory, number] {\n const text = `${title} ${body}`.toLowerCase();\n const scores: Record<IssueCategory, number> = {\n bug: 0,\n feature: 0,\n question: 0,\n documentation: 0,\n security: 0,\n performance: 0,\n };\n\n let totalMatches = 0;\n\n for (const [category, keywords] of Object.entries(CATEGORY_KEYWORDS)) {\n for (const keyword of keywords) {\n if (text.includes(keyword)) {\n scores[category as IssueCategory]++;\n totalMatches++;\n }\n }\n }\n\n // Find category with highest score\n let bestCategory: IssueCategory = 'bug';\n let bestScore = 0;\n for (const [category, score] of Object.entries(scores)) {\n if (score > bestScore) {\n bestScore = score;\n bestCategory = category as IssueCategory;\n }\n }\n\n // Confidence: ratio of best score to total matches, minimum 0.1\n const confidence = totalMatches > 0 ? Math.min(bestScore / totalMatches, 1) : 0.1;\n\n return [bestCategory, Math.round(confidence * 100) / 100];\n}\n\n// ============================================================================\n// Label Extraction\n// ============================================================================\n\n/**\n * Common label patterns that can be extracted from issue text.\n */\nconst LABEL_HINTS: ReadonlyMap<string, string> = new Map([\n ['bug', 'bug'],\n ['feature request', 'enhancement'],\n ['enhancement', 'enhancement'],\n ['breaking change', 'breaking-change'],\n ['documentation', 'documentation'],\n ['help wanted', 'help wanted'],\n ['good first issue', 'good first issue'],\n ['security', 'security'],\n ['performance', 'performance'],\n ['regression', 'regression'],\n]);\n\n/**\n * Extracts suggested labels from issue title and body text.\n *\n * @param title - Issue title\n * @param body - Issue body\n * @returns Array of suggested label strings (max 5)\n */\nexport function extractLabelsFromBody(title: string, body: string): string[] {\n const text = `${title} ${body}`.toLowerCase();\n const labels: string[] = [];\n\n for (const [pattern, label] of LABEL_HINTS) {\n if (text.includes(pattern) && !labels.includes(label)) {\n labels.push(label);\n }\n }\n\n return labels.slice(0, 5);\n}\n\n/** Maps SCM comment details to the triage representation. */\nexport function mapIssueComments(comments: readonly ScmCommentDetail[]): IssueComment[] {\n return comments.map((comment) => ({\n id: comment.id,\n body: comment.body,\n author: comment.author,\n authorAssociation: comment.authorAssociation,\n createdAt: comment.createdAt,\n }));\n}\n\n/** Creates a human-readable description for a typed action. */\nexport function describeAction(action: AgentAction): string {\n switch (action.type) {\n case 'ClassifyIssue':\n return `Classified as ${action.category} (${String(Math.round(action.confidence * 100))}% confidence)`;\n case 'ProposeLabels':\n return `Suggest labels: ${action.labels.join(', ')}`;\n case 'SummarizeIssue':\n return action.summary.slice(0, 100);\n default:\n return `${action.type} action`;\n }\n}\n\n/** Builds details object for a proposed action. */\nexport function buildActionDetails(\n action: AgentAction,\n policy: { allowed: boolean; violations: readonly { rule: string; message: string }[] },\n corrob: CorroborationResult\n): Record<string, unknown> {\n return {\n policyViolations: policy.violations.map((violation) => violation.rule),\n missingCorroboration: corrob.missing,\n ...(action.type === 'ClassifyIssue' && { category: action.category }),\n ...(action.type === 'ProposeLabels' && { labels: action.labels }),\n };\n}\n\n// ============================================================================\n// Result Formatting\n// ============================================================================\n\n/**\n * Formats a triage result as a GitHub markdown comment.\n *\n * @param result - The complete triage result\n * @returns Formatted markdown string\n */\nexport function formatTriageComment(result: IssueTriageResult): string {\n const emoji = CATEGORY_EMOJI[result.category];\n const categoryName = CATEGORY_DISPLAY_NAMES[result.category];\n const lines: string[] = [];\n\n lines.push(`## ${emoji} Issue Triage: ${categoryName}`);\n lines.push('');\n lines.push(\n `**Category:** ${categoryName} (${String(Math.round(result.categoryConfidence * 100))}% confidence)`\n );\n lines.push(\n `**Trust Tier:** ${result.trustAssessment.trustTier} (${result.trustAssessment.userRole})`\n );\n\n if (result.trustAssessment.reputationScore !== undefined) {\n lines.push(`**Reputation Score:** ${String(result.trustAssessment.reputationScore)}/100`);\n }\n\n if (result.trustAssessment.isSuspicious) {\n lines.push('');\n lines.push(':warning: **Suspicious signals detected:**');\n for (const signal of result.trustAssessment.suspiciousSignals) {\n lines.push(`- ${signal}`);\n }\n }\n\n if (result.proposedActions.length > 0) {\n lines.push('');\n lines.push('### Proposed Actions');\n lines.push('');\n for (const action of result.proposedActions) {\n const status = formatActionStatus(action);\n lines.push(`- ${status} **${action.type}**: ${action.description}`);\n }\n }\n\n lines.push('');\n lines.push(`---`);\n lines.push(`_Triage completed in ${String(result.totalDurationMs)}ms_`);\n\n return lines.join('\\n');\n}\n\n/**\n * Formats the policy/corroboration status of an action.\n */\nfunction formatActionStatus(action: ProposedAction): string {\n if (action.policyApproved && action.corroborated) return ':white_check_mark:';\n if (action.policyApproved && !action.corroborated) return ':yellow_circle:';\n return ':no_entry:';\n}\n","/**\n * nexus-agents/dogfooding - Issue Triage Processor\n *\n * Deterministic GitHub issue triage using the full security pipeline:\n * - Input sanitization (input-sanitizer.ts)\n * - Trust classification (trust-classifier.ts)\n * - Reputation assessment (reputation-model.ts) [NEW — #828]\n * - Typed agent actions (action-schema.ts)\n * - Policy gate validation (policy-gate.ts)\n * - Corroboration validation (corroboration-validator.ts) [NEW — #828]\n *\n * Read-only by default (dryRun: true). Proposes actions but never\n * auto-posts to GitHub without explicit opt-in.\n *\n * @module dogfooding/issue-triage\n * (Source: Issue #828 — Wire remaining security modules)\n */\n\nimport type { Result } from '../core/index.js';\nimport { ok, err, createLogger, getTimeProvider } from '../core/index.js';\nimport { sanitizeInput } from '../security/input-sanitizer.js';\nimport { hasToken } from '../scm/token-resolver.js';\nimport type { ClassifyResult } from '../security/trust-classifier.js';\nimport type { FirewallResult } from '../security/firewall/firewall-pipeline.js';\nimport { runUntrustedInputFirewall } from './untrusted-input-firewall.js';\nimport type { TrustTier } from '../security/trust-types.js';\nimport { evaluatePolicy } from '../security/policy-gate.js';\nimport type { ActionContext } from '../security/policy-gate.js';\nimport { validateCorroboration } from '../security/corroboration-validator.js';\nimport { assessReputation, ReputationCache } from '../security/reputation-model.js';\nimport type {\n ReputationAssessment,\n GitHubUserMetadata,\n ReputationGateDecision,\n} from '../security/reputation-model.js';\nimport type { AgentAction, SourceCitation } from '../security/action-schema.js';\nimport { parseIssueUrl } from '../scm/url-parsers.js';\nimport { createFullGitHubProvider } from '../scm/github-provider-traits.js';\nimport type { ScmUserMetadata } from '../scm/types.js';\nimport {\n buildActionDetails,\n categorizeIssue,\n describeAction,\n extractLabelsFromBody,\n mapIssueComments,\n} from './issue-triage-helpers.js';\nimport type {\n IssueMetadata,\n IssueComment,\n IssueTriageConfig,\n IssueTriageResult,\n ProposedAction,\n TrustAssessment,\n} from './issue-triage-types.js';\nimport { DEFAULT_ISSUE_TRIAGE_CONFIG } from './issue-triage-types.js';\n\nconst logger = createLogger({ component: 'IssueTriage' });\n\n/**\n * Applies the two safety actions the untrusted-input rules mandate (#4667).\n *\n * They are mutually exclusive by construction: a hostile tier refuses, a\n * suspicious-but-not-hostile one escalates. Emitting both would ask a human to\n * approve something the system has already declined.\n */\nfunction applySafetyActions(\n actions: readonly AgentAction[],\n gateDecision: ReputationGateDecision,\n reputation: ReputationAssessment | undefined,\n issue: IssueMetadata\n): AgentAction[] {\n const withRefusal = appendRefusalIfHostile(actions, gateDecision, issue);\n return appendApprovalIfBorderline(withRefusal, gateDecision, reputation, issue);\n}\n\n/**\n * Appends a `RequestHumanApproval` for suspicious-but-not-hostile input (#4667).\n *\n * Triage was binary: tier 4 refuses, everything else proceeds. That left no way\n * to express the common real case — reputation noticed something (a new\n * account, rapid comments, a weak signal) without it amounting to hostility.\n * Those proposals are not safe to auto-apply and not fair to refuse, so they\n * escalate.\n *\n * Deliberately mutually exclusive with the refusal: a hostile tier already\n * emits `RefuseAction`, and stacking \"refused\" with \"please approve\" would ask\n * a human to approve something the system just declined.\n */\nfunction appendApprovalIfBorderline(\n actions: readonly AgentAction[],\n gateDecision: ReputationGateDecision,\n reputation: ReputationAssessment | undefined,\n issue: IssueMetadata\n): AgentAction[] {\n if (gateDecision.enforcedTier === '4') return [...actions];\n // Tier 1 is the allowlist escape hatch — asking a maintainer to approve a\n // maintainer's own issue is noise, and it would undo the \"allowlist wins\"\n // guarantee that tier exists to provide.\n if (gateDecision.enforcedTier === '1') return [...actions];\n if (reputation?.isSuspicious !== true) return [...actions];\n\n // `no_prior_contributions` counts the author's comments ON THIS ISSUE\n // (`countAuthorComments(author, comments)`), so a freshly filed issue always\n // trips it — it is true for essentially every new issue and therefore carries\n // no information. Escalating on it alone would escalate on everything, which\n // is how an approval gate becomes noise and then gets ignored. Require at\n // least one signal that actually distinguishes this author.\n const distinguishing = reputation.suspiciousSignals.filter(\n (sig) => sig !== 'no_prior_contributions'\n );\n if (distinguishing.length === 0) return [...actions];\n\n const signals = distinguishing.join(', ');\n return [\n ...actions,\n {\n type: 'RequestHumanApproval',\n reason:\n `Issue #${String(issue.number)} carries suspicious reputation signals but did not ` +\n `reach the hostile tier. Proposed actions need a human decision.`,\n context:\n `Author ${issue.author} at enforced tier ${gateDecision.enforcedTier} ` +\n `(${gateDecision.mode} mode). Signals: ${signals.length > 0 ? signals : '(none recorded)'}.`,\n },\n ];\n}\n\n/**\n * Appends an explicit `RefuseAction` when the enforced tier is hostile (#4667).\n *\n * The generated actions are kept rather than replaced: they are the audit trail\n * of what was declined, and every one of them is already blocked by the policy\n * gate at tier 4. What was missing is a positive statement that the input was\n * refused and where to escalate it.\n */\nfunction appendRefusalIfHostile(\n actions: readonly AgentAction[],\n gateDecision: ReputationGateDecision,\n issue: IssueMetadata\n): AgentAction[] {\n if (gateDecision.enforcedTier !== '4') return [...actions];\n return [\n ...actions,\n {\n type: 'RefuseAction',\n reason:\n `Issue #${String(issue.number)} was classified at hostile trust tier 4 ` +\n `(${gateDecision.mode} mode). Automated triage actions are withheld pending human review.`,\n escalateTo: 'security',\n },\n ];\n}\n\n/**\n * GitHub issue triage processor.\n *\n * Wires all 8 security modules into a read-only triage pipeline:\n * 1. Fetch issue + comments from GitHub API\n * 2. Sanitize untrusted content (input-sanitizer)\n * 3. Classify author trust (trust-classifier)\n * 4. Assess author reputation (reputation-model) [#828]\n * 5. Generate typed actions (action-schema)\n * 6. Validate through policy gate (policy-gate)\n * 7. Validate corroboration (corroboration-validator) [#828]\n * 8. Return proposed actions\n */\n/** Warn when a demotion was computed but the gating mode did not enforce it (#3122). */\nfunction logSuppressedDemotion(\n issueNumber: number,\n author: string,\n trustResult: ClassifyResult,\n gateDecision: ReputationGateDecision\n): void {\n if (!gateDecision.demotionSuppressed) return;\n logger.warn('Reputation demotion suppressed by gating mode (would block under enforce)', {\n issueNumber,\n author,\n mode: gateDecision.mode,\n classifierTier: trustResult.trustTier,\n reconciledTier: gateDecision.reconciledTier,\n });\n}\n\nexport class IssueTriage {\n private readonly config: IssueTriageConfig;\n private readonly reputationCache: ReputationCache;\n\n constructor(config?: Partial<IssueTriageConfig>) {\n this.config = { ...DEFAULT_ISSUE_TRIAGE_CONFIG, ...config };\n this.reputationCache = new ReputationCache();\n }\n\n /**\n * Triages a GitHub issue through the full security pipeline.\n */\n async triageIssue(issueUrl: string): Promise<Result<IssueTriageResult, Error>> {\n const startTime = getTimeProvider().now();\n\n logger.info('Starting issue triage', { issueUrl });\n\n const parseResult = parseIssueUrl(issueUrl);\n if (!parseResult.ok) return parseResult;\n\n const { owner, repo, issueNumber } = parseResult.value;\n\n const fetchResult = await this.fetchIssueData(owner, repo, issueNumber);\n if (!fetchResult.ok) return fetchResult;\n\n const { issue: issueResult, existingLabels } = fetchResult.value;\n\n // Sanitize untrusted content (Issue #828 — input-sanitizer wiring)\n const safeTitle = this.sanitizeContent(issueResult.title, issueResult.author);\n const safeBody = this.sanitizeContent(issueResult.body, issueResult.author);\n\n // Reputation is measured here, with data the firewall cannot see (account\n // age, comment history), and handed to the firewall per call.\n const reputation = this.assessAuthorReputation(fetchResult.value);\n\n // #4992: classify through the shared HostileInputFirewall so the trust\n // decision reaches the security audit trail. Signal-only under the default\n // NEXUS_FIREWALL_POLICY=off; `validateActions` below stays the Rule-of-Two\n // enforcement point. The firewall runs the ONE reputation gate (#3122:\n // off/audit/enforce; audit reports a would-be demotion without enforcing\n // it; the allowlist Tier 1 remains the escape hatch), so its Rule-of-Two\n // signal and this path's policy gate act on the same enforced tier.\n const firewall = this.classifyAuthor(issueResult, reputation);\n if (!firewall.ok) return firewall;\n const trustResult = firewall.value.trust;\n const gateDecision = firewall.value.reputationGate;\n if (gateDecision === undefined) {\n // Structurally unreachable — the option is always passed — but a missing\n // gate must not be papered over with the classifier tier.\n return err(new Error('Untrusted-input firewall returned no reputation gate decision'));\n }\n logSuppressedDemotion(issueNumber, issueResult.author, trustResult, gateDecision);\n\n // Generate and validate actions, at the tier the gate ENFORCED (#5719)\n const enforced = { ...trustResult, trustTier: gateDecision.enforcedTier };\n const actions = this.generateActions(safeTitle, safeBody, issueResult, enforced);\n // #4667: a hostile enforced tier must produce an explicit refusal. Before\n // this, tier 4 merely meant every generated action failed the policy gate —\n // the caller saw fewer approved actions and no statement that anything was\n // refused, so the fail-closed escalation the rules mandate had no producer.\n // RefuseAction is tier-4 \"always allowed\" (trust-classifier.ts:170), so it\n // survives the gate that blocks the rest.\n const withEscalation = applySafetyActions(actions, gateDecision, reputation, issueResult);\n const validatedActions = this.validateActions(withEscalation, gateDecision, existingLabels);\n\n const result = this.buildResult({\n issue: issueResult,\n actions: validatedActions,\n trustResult,\n isAllowlisted: firewall.value.isAllowlisted,\n auditSink: firewall.value.auditSink,\n reputation,\n gateDecision,\n safeContent: { title: safeTitle, body: safeBody },\n startTime,\n });\n\n logger.info('Issue triage completed', {\n issueNumber,\n category: result.category,\n actionCount: result.proposedActions.length,\n durationMs: result.totalDurationMs,\n });\n\n return ok(result);\n }\n\n /**\n * Sanitizes untrusted issue content before processing.\n * (Source: Issue #828 — input-sanitizer wiring)\n */\n private sanitizeContent(text: string, author: string): string {\n if (text.length === 0) return text;\n const result = sanitizeInput(text, 'unknown', author);\n if (result.wasModified) {\n logger.warn('Issue content sanitized', {\n author,\n strippedCount: result.strippedElements.length,\n injectionFlags: result.injectionFlags,\n });\n }\n return result.content;\n }\n\n /**\n * Classifies the issue author's trust tier through the shared firewall\n * (#4992; originally Issue #828 — trust-classifier wiring).\n *\n * The access posture is passed per call so the firewall's Rule-of-Two check\n * sees the same facts `validateActions` enforces on. No maintainer allowlist\n * is passed: there is no source for one today (no config field, no env var),\n * so none is consulted and `isAllowlisted` stays absent on the result rather\n * than being recorded as `false`.\n */\n private classifyAuthor(\n issue: IssueMetadata,\n reputation: ReputationAssessment | undefined\n ): Result<FirewallResult, Error> {\n return runUntrustedInputFirewall(\n {\n type: 'issue',\n username: issue.author,\n authorAssociation: issue.authorAssociation,\n title: issue.title,\n body: issue.body,\n },\n { context: this.accessContext(), reputation: { assessment: reputation } }\n );\n }\n\n /**\n * The access posture this triage runs under — the two Rule-of-Two conjuncts\n * that are about the agent rather than the input. Shared by the firewall\n * (#4992) and `validateActions` so the two cannot disagree.\n */\n private accessContext(): { hasWriteAccess: boolean; hasSecretAccess: boolean } {\n return {\n hasWriteAccess: !this.config.dryRun,\n // #4681: read the token from where it ACTUALLY comes from. The previous\n // form (`this.config.githubToken !== undefined`) was written to make this\n // conjunct \"real\", but no production caller ever sets `githubToken` — the\n // SCM provider resolves the live credential from GITHUB_TOKEN/GH_TOKEN.\n // So the conjunct was permanently false and Rule of Two could never trip,\n // which is precisely the constant it was introduced to remove.\n // `hasToken()` is the canonical resolver-backed check.\n hasSecretAccess: this.config.githubToken !== undefined || hasToken('github'),\n };\n }\n\n /**\n * Assesses the issue author's reputation using the reputation model.\n * This is one of the two NEW security module wirings completing #828.\n * (Source: Issue #828 — reputation-model wiring)\n */\n private assessAuthorReputation(data: {\n issue: IssueMetadata;\n comments: readonly IssueComment[];\n commentsAvailable: boolean;\n accountAgeDays?: number;\n }): ReputationAssessment | undefined {\n if (!this.config.enableReputation) return undefined;\n const { issue, comments, commentsAvailable, accountAgeDays } = data;\n\n // #4681: scan BOTH the title and the body. Scanning only the body left a\n // real bypass: an injection payload is plain text, so content-sanitization\n // does not strip it from the title, and its flags were discarded here — the\n // identical payload was refused at Tier 4 in the body but raised no signal\n // at all in the title, then reached the emitted SummarizeIssue verbatim.\n const bodyScan = sanitizeInput(issue.body, 'unknown', issue.author);\n const titleScan = sanitizeInput(issue.title, 'unknown', issue.author);\n const injectionFlags = [...new Set([...titleScan.injectionFlags, ...bodyScan.injectionFlags])];\n\n // #3121: accountAgeDays is the author's REAL account age (fetched in\n // fetchIssueData) or undefined when the lookup failed. When undefined it is\n // OMITTED so the engine skips the new_account signal (#3106) — never\n // fabricated. priorContributions/recentCommentCount use real comment data.\n const metadata: GitHubUserMetadata = {\n username: issue.author,\n ...(accountAgeDays !== undefined ? { accountAgeDays } : {}),\n ...(commentsAvailable\n ? {\n priorContributions: countAuthorComments(issue.author, comments),\n recentCommentCount: countRecentComments(issue.author, comments),\n recentCommentWindowMinutes: 10,\n }\n : {}),\n authorAssociation: issue.authorAssociation,\n injectionFlags,\n };\n\n return assessReputation(metadata, commentsAvailable ? this.reputationCache : undefined);\n }\n\n /**\n * Generates typed agent actions based on issue analysis.\n */\n private generateActions(\n safeTitle: string,\n safeBody: string,\n issue: IssueMetadata,\n /**\n * The classify result with `trustTier` RECONCILED to the tier the policy\n * gate actually enforced (#5719). Stamping a citation with the\n * pre-reputation tier makes the record disagree with what anyone enforced,\n * and `checkSourceTrustTiers` inspects only `issueComment`, so nothing\n * catches it. `userRole` is still the classifier's — that is what it names.\n */\n trustResult: ClassifyResult\n ): AgentAction[] {\n const actions: AgentAction[] = [];\n const source = this.createIssueSource(issue, trustResult.trustTier);\n\n // Always generate ClassifyIssue action\n const [category, confidence] = categorizeIssue(safeTitle, safeBody);\n actions.push({\n type: 'ClassifyIssue',\n category,\n confidence,\n sources: [source],\n });\n\n // Generate ProposeLabels if we found label hints\n const labels = extractLabelsFromBody(safeTitle, safeBody);\n if (labels.length > 0) {\n actions.push({\n type: 'ProposeLabels',\n labels,\n reason: `Keyword-based label extraction from issue #${String(issue.number)}`,\n sources: [source],\n });\n }\n\n // Generate SummarizeIssue for semi-trusted+ authors\n if (trustResult.trustTier === '1' || trustResult.trustTier === '2') {\n const summary = `Issue #${String(issue.number)} \"${safeTitle}\" by ${issue.author} (${trustResult.userRole})`;\n actions.push({\n type: 'SummarizeIssue',\n summary: summary.length >= 10 ? summary : `${summary} — awaiting further analysis`,\n sources: [source],\n });\n }\n\n return actions;\n }\n\n /**\n * Validates all actions through policy gate and corroboration validator.\n * This completes the #828 wiring by integrating corroboration-validator.\n * (Source: Issue #828 — policy-gate + corroboration-validator wiring)\n */\n private validateActions(\n actions: readonly AgentAction[],\n gateDecision: ReputationGateDecision,\n existingLabels: ReadonlySet<string> | undefined\n ): ProposedAction[] {\n // #3119 + #3122: reputation GATES via the rollout mode. `enforce` uses the\n // reconciled (possibly demoted) tier; `audit`/`off` enforce the classifier\n // tier (the suppressed demotion is logged upstream). Demotion-only;\n // Tier-1/allowlist always wins.\n const context: ActionContext = {\n inputTrustTier: gateDecision.enforcedTier,\n ...this.accessContext(),\n ...(existingLabels !== undefined ? { existingLabels } : {}),\n };\n\n return actions.map((action) => {\n const policyDecision = evaluatePolicy(action, context);\n const corrobResult = validateCorroboration(action);\n\n return {\n type: action.type,\n description: describeAction(action),\n policyApproved: policyDecision.allowed,\n corroborated: corrobResult.satisfied,\n details: buildActionDetails(action, policyDecision, corrobResult),\n };\n });\n }\n\n /**\n * Builds the final triage result.\n */\n private buildResult(opts: {\n issue: IssueMetadata;\n actions: readonly ProposedAction[];\n trustResult: ClassifyResult;\n /** Present only when the firewall consulted an allowlist (#4992). */\n isAllowlisted: boolean | undefined;\n auditSink: FirewallResult['auditSink'];\n reputation: ReputationAssessment | undefined;\n gateDecision: ReputationGateDecision;\n safeContent: { title: string; body: string };\n startTime: number;\n }): IssueTriageResult {\n const {\n issue,\n actions,\n trustResult,\n isAllowlisted,\n auditSink,\n reputation,\n gateDecision,\n safeContent,\n startTime,\n } = opts;\n const [category, confidence] = categorizeIssue(safeContent.title, safeContent.body);\n\n // Tier 1 actors (owner/maintainer) cannot be suspicious — reconcile\n // the trust-classifier result with the reputation-model result.\n const isTier1 = trustResult.trustTier === '1';\n\n const trustAssessment: TrustAssessment = {\n trustTier: trustResult.trustTier,\n userRole: trustResult.userRole,\n // Measured or absent (#4992): never the classifier's default `false`.\n ...(isAllowlisted !== undefined ? { isAllowlisted } : {}),\n auditSink,\n reputationScore: reputation?.reputationScore,\n coverage: reputation?.coverage,\n suspiciousSignals: isTier1 ? [] : (reputation?.suspiciousSignals ?? []),\n isSuspicious: isTier1 ? false : (reputation?.isSuspicious ?? false),\n // #3122: surface both the enforced tier (what the gate used) and the\n // reconciled tier (what reputation computed) so telemetry can't mistake\n // a would-be demotion for an enforced one.\n enforcedTrustTier: gateDecision.enforcedTier,\n reputationReconciledTier: gateDecision.reconciledTier,\n gatingMode: gateDecision.mode,\n };\n\n return {\n issueNumber: issue.number,\n repository: `${issue.owner}/${issue.repo}`,\n proposedActions: actions,\n trustAssessment,\n category,\n categoryConfidence: confidence,\n totalDurationMs: getTimeProvider().now() - startTime,\n timestamp: getTimeProvider().nowIso(),\n };\n }\n\n /**\n * Creates a repo file source citation for the triage.\n */\n private createIssueSource(issue: IssueMetadata, trustTier: TrustTier): SourceCitation {\n return {\n type: 'issueBody',\n issueNumber: issue.number,\n author: issue.author,\n authorTrustTier: trustTier,\n };\n }\n\n /**\n * Fetches issue data from the SCM provider and maps to dogfooding types.\n */\n private async fetchIssueData(\n owner: string,\n repo: string,\n issueNumber: number\n ): Promise<\n Result<\n {\n issue: IssueMetadata;\n comments: IssueComment[];\n commentsAvailable: boolean;\n accountAgeDays?: number;\n existingLabels?: ReadonlySet<string>;\n },\n Error\n >\n > {\n const provider = createFullGitHubProvider(`${owner}/${repo}`);\n\n const detailResult = await provider.getIssueDetail(issueNumber);\n if (!detailResult.ok) return err(detailResult.error);\n\n const detail = detailResult.value;\n const issue: IssueMetadata = { ...detail, owner, repo };\n\n const commentsResult = await provider.listCommentDetails(issueNumber);\n if (!commentsResult.ok) {\n logger.warn('Failed to fetch issue comments; activity reputation is unmeasured', {\n issueNumber,\n error: commentsResult.error.message,\n });\n }\n const comments = commentsResult.ok ? mapIssueComments(commentsResult.value) : [];\n const commentsAvailable = commentsResult.ok;\n\n const labelsResult = await provider.listRepositoryLabels();\n if (!labelsResult.ok) {\n logger.warn('Failed to fetch repository labels; label validity is unmeasured', {\n issueNumber,\n error: labelsResult.error.message,\n });\n }\n\n // #3121: fetch the author's REAL account age (their account creation date,\n // not the issue date). Best-effort — on failure or an unparseable date we\n // omit it, so the reputation engine SKIPS the new_account signal (#3106)\n // rather than fabricating a value. Reputation must not block on this.\n const accountAgeDays = await this.fetchAccountAgeDays(provider, detail.author);\n\n return ok({\n issue,\n comments,\n commentsAvailable,\n ...(accountAgeDays !== undefined ? { accountAgeDays } : {}),\n ...(labelsResult.ok ? { existingLabels: new Set(labelsResult.value) } : {}),\n });\n }\n\n /**\n * Best-effort lookup of a GitHub user's account age in days. Returns\n * `undefined` if the user-metadata fetch fails or the creation date can't be\n * parsed (#3121) — callers must treat absence as \"unknown\", not \"benign\".\n */\n private async fetchAccountAgeDays(\n provider: { fetchUserMetadata: (u: string) => Promise<Result<ScmUserMetadata, Error>> },\n username: string\n ): Promise<number | undefined> {\n try {\n const result = await provider.fetchUserMetadata(username);\n if (!result.ok) return undefined;\n const createdMs = Date.parse(result.value.createdAt);\n if (!Number.isFinite(createdMs)) return undefined;\n return Math.floor((getTimeProvider().now() - createdMs) / 86_400_000);\n } catch {\n // Truly best-effort: even an unexpected rejection (token resolution,\n // transport) must not break triage — fall back to \"unknown\" age.\n return undefined;\n }\n }\n}\n\n/** Counts how many comments the author has made on the issue. */\nfunction countAuthorComments(author: string, comments: readonly IssueComment[]): number {\n return comments.filter((comment) => comment.author === author).length;\n}\n\n/** Counts recent comments from the author (within 10 minutes). */\nfunction countRecentComments(author: string, comments: readonly IssueComment[]): number {\n const tenMinutesAgo = Date.now() - 10 * 60 * 1000;\n return comments.filter(\n (comment) => comment.author === author && new Date(comment.createdAt).getTime() > tenMinutesAgo\n ).length;\n}\n\n/**\n * Creates an IssueTriage instance.\n */\nexport function createIssueTriage(config?: Partial<IssueTriageConfig>): IssueTriage {\n return new IssueTriage(config);\n}\n"],"mappings":"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","names":["logger","z","z","z","z","z","z","logger","z","z","logger","logger","logger","logger","z","z","logger"]}
|