nexus-agents 3.11.0 → 3.12.1

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.
Files changed (122) hide show
  1. package/dist/{child-mcp-config-MJNB5GXS.js → child-mcp-config-5SWBEZPD.js} +2 -2
  2. package/dist/{chunk-M3HKW5HF.js → chunk-2ZVGB7AJ.js} +2 -2
  3. package/dist/{chunk-ERCCVO4N.js → chunk-3W7ST3AB.js} +2 -2
  4. package/dist/{chunk-7FJFN74H.js → chunk-5HGMO3W3.js} +5 -5
  5. package/dist/{chunk-32AWWH4F.js → chunk-5TE3ORD5.js} +2 -2
  6. package/dist/{chunk-JBLM3UZA.js → chunk-5XTQIBA3.js} +7 -7
  7. package/dist/{chunk-N62DDWV3.js → chunk-6GKG5U5Y.js} +2 -2
  8. package/dist/{chunk-K3TRG2CD.js → chunk-7WIBQZR6.js} +3 -3
  9. package/dist/{chunk-DMKV7ANB.js → chunk-ABU3SZXU.js} +8 -8
  10. package/dist/{chunk-JBSL2HKV.js → chunk-AV3NVATT.js} +3 -3
  11. package/dist/{chunk-ZVZHNAQH.js → chunk-BMLBXCC3.js} +2 -2
  12. package/dist/{chunk-QUOCOEDQ.js → chunk-C4BSQH3A.js} +4 -4
  13. package/dist/{chunk-MXJEMYC7.js → chunk-CDWKQ5FV.js} +2 -2
  14. package/dist/{chunk-X2HMEG5F.js → chunk-CODRIZMT.js} +3 -3
  15. package/dist/{chunk-X2HMEG5F.js.map → chunk-CODRIZMT.js.map} +1 -1
  16. package/dist/{chunk-FJKMNLTU.js → chunk-D34SUVBO.js} +4 -4
  17. package/dist/{chunk-TJAMRU4T.js → chunk-EQDV4YIW.js} +36 -69
  18. package/dist/chunk-EQDV4YIW.js.map +1 -0
  19. package/dist/{chunk-G5GOWK5T.js → chunk-FWBR5P72.js} +2 -2
  20. package/dist/{chunk-R4ULM2GB.js → chunk-GO3GAPVQ.js} +5 -5
  21. package/dist/{chunk-3WWE26PG.js → chunk-GSRW2H3C.js} +2 -2
  22. package/dist/{chunk-JJZF37X2.js → chunk-HVFUWGGB.js} +3 -3
  23. package/dist/{chunk-OL6TRWBA.js → chunk-JHL7JLRJ.js} +2 -2
  24. package/dist/{chunk-KLMXZRDK.js → chunk-JJAEGFPS.js} +2 -2
  25. package/dist/{chunk-MAP3BVCT.js → chunk-KHYSK52P.js} +2 -2
  26. package/dist/{chunk-6VUIQQ3O.js → chunk-KTYTESEV.js} +101 -5
  27. package/dist/chunk-KTYTESEV.js.map +1 -0
  28. package/dist/{chunk-G6AVXG6F.js → chunk-LI5RF3H7.js} +2 -2
  29. package/dist/{chunk-M7566JRS.js → chunk-NGQC5KIB.js} +8 -8
  30. package/dist/{chunk-3UEZBTLV.js → chunk-OJPYOK4V.js} +3 -3
  31. package/dist/{chunk-TU4FRZWU.js → chunk-PAJXMEHU.js} +2 -2
  32. package/dist/{chunk-7H2AKJVR.js → chunk-Q4E6MPAW.js} +70 -42
  33. package/dist/{chunk-7H2AKJVR.js.map → chunk-Q4E6MPAW.js.map} +1 -1
  34. package/dist/{chunk-SOCKDRK6.js → chunk-RGR22IN6.js} +4 -4
  35. package/dist/{chunk-KPYPJGSD.js → chunk-TPU5DT74.js} +7 -7
  36. package/dist/{chunk-SCI6FKPM.js → chunk-TRZTLCL4.js} +8 -8
  37. package/dist/{chunk-VF4ZV4KG.js → chunk-VJ2LZ2ZM.js} +2 -2
  38. package/dist/{chunk-TJ6UFU3I.js → chunk-XXOE3OHH.js} +2 -2
  39. package/dist/{cli-circuit-breaker-JWCKCVDX.js → cli-circuit-breaker-WKUYRX5M.js} +3 -3
  40. package/dist/cli.js +43 -43
  41. package/dist/{composite-router-5OCKHBR3.js → composite-router-SKXMG4DA.js} +2 -2
  42. package/dist/{consensus-vote-HG5BE56Y.js → consensus-vote-FGB637PZ.js} +16 -16
  43. package/dist/{context-retriever-JNXUAPWJ.js → context-retriever-NK6O77TT.js} +8 -8
  44. package/dist/{doctor-deep-IZAAFLBC.js → doctor-deep-EJAW6SI5.js} +3 -3
  45. package/dist/{doctor-live-Z5IP2TWY.js → doctor-live-26YJA5Z4.js} +9 -9
  46. package/dist/{expert-bridge-XI4BNDI7.js → expert-bridge-PNXWIDQV.js} +4 -4
  47. package/dist/{factory-WU2VY6QI.js → factory-KVIEFTAC.js} +5 -5
  48. package/dist/{factory-QAH46LJX.js → factory-MXWNULXD.js} +9 -9
  49. package/dist/{improvement-review-E2PK2AEA.js → improvement-review-HKJNXMZM.js} +5 -5
  50. package/dist/index.js +31 -31
  51. package/dist/{init-opencode-BQHWMUOD.js → init-opencode-7IU4SWTC.js} +6 -6
  52. package/dist/issue-triage-EKFJQZFJ.js +17 -0
  53. package/dist/{pr-reviewer-helpers-NXG7X3WB.js → pr-reviewer-helpers-SUMMYYF7.js} +4 -4
  54. package/dist/{registry-command-J2FV4BT2.js → registry-command-XFH3M634.js} +2 -2
  55. package/dist/{repo-security-plan-IRILDORZ.js → repo-security-plan-RCYKT5GS.js} +3 -3
  56. package/dist/{research-helpers-synthesize-KUZRNHXY.js → research-helpers-synthesize-BVJB4HW5.js} +4 -4
  57. package/dist/{routing-memory-GFO76574.js → routing-memory-AR4YR2RL.js} +2 -2
  58. package/dist/{session-memory-CY3T3XNO.js → session-memory-UYJU63EG.js} +3 -3
  59. package/dist/{setup-command-GBCILO74.js → setup-command-LWHN672O.js} +14 -14
  60. package/dist/{setup-config-A27SDCRY.js → setup-config-XZMHAOMR.js} +3 -3
  61. package/dist/{setup-custom-api-UZV66PZA.js → setup-custom-api-BWX47BKD.js} +4 -4
  62. package/dist/{tool-memory-YYEWKSC4.js → tool-memory-DB3YDR5W.js} +5 -5
  63. package/dist/{unified-registry-PKDBZ6FO.js → unified-registry-XSY5XAIZ.js} +10 -10
  64. package/dist/{weather-report-MXQTPLDG.js → weather-report-C2P6NHTX.js} +2 -2
  65. package/package.json +1 -1
  66. package/dist/chunk-6VUIQQ3O.js.map +0 -1
  67. package/dist/chunk-TJAMRU4T.js.map +0 -1
  68. package/dist/issue-triage-6STMHG7L.js +0 -18
  69. /package/dist/{child-mcp-config-MJNB5GXS.js.map → child-mcp-config-5SWBEZPD.js.map} +0 -0
  70. /package/dist/{chunk-M3HKW5HF.js.map → chunk-2ZVGB7AJ.js.map} +0 -0
  71. /package/dist/{chunk-ERCCVO4N.js.map → chunk-3W7ST3AB.js.map} +0 -0
  72. /package/dist/{chunk-7FJFN74H.js.map → chunk-5HGMO3W3.js.map} +0 -0
  73. /package/dist/{chunk-32AWWH4F.js.map → chunk-5TE3ORD5.js.map} +0 -0
  74. /package/dist/{chunk-JBLM3UZA.js.map → chunk-5XTQIBA3.js.map} +0 -0
  75. /package/dist/{chunk-N62DDWV3.js.map → chunk-6GKG5U5Y.js.map} +0 -0
  76. /package/dist/{chunk-K3TRG2CD.js.map → chunk-7WIBQZR6.js.map} +0 -0
  77. /package/dist/{chunk-DMKV7ANB.js.map → chunk-ABU3SZXU.js.map} +0 -0
  78. /package/dist/{chunk-JBSL2HKV.js.map → chunk-AV3NVATT.js.map} +0 -0
  79. /package/dist/{chunk-ZVZHNAQH.js.map → chunk-BMLBXCC3.js.map} +0 -0
  80. /package/dist/{chunk-QUOCOEDQ.js.map → chunk-C4BSQH3A.js.map} +0 -0
  81. /package/dist/{chunk-MXJEMYC7.js.map → chunk-CDWKQ5FV.js.map} +0 -0
  82. /package/dist/{chunk-FJKMNLTU.js.map → chunk-D34SUVBO.js.map} +0 -0
  83. /package/dist/{chunk-G5GOWK5T.js.map → chunk-FWBR5P72.js.map} +0 -0
  84. /package/dist/{chunk-R4ULM2GB.js.map → chunk-GO3GAPVQ.js.map} +0 -0
  85. /package/dist/{chunk-3WWE26PG.js.map → chunk-GSRW2H3C.js.map} +0 -0
  86. /package/dist/{chunk-JJZF37X2.js.map → chunk-HVFUWGGB.js.map} +0 -0
  87. /package/dist/{chunk-OL6TRWBA.js.map → chunk-JHL7JLRJ.js.map} +0 -0
  88. /package/dist/{chunk-KLMXZRDK.js.map → chunk-JJAEGFPS.js.map} +0 -0
  89. /package/dist/{chunk-MAP3BVCT.js.map → chunk-KHYSK52P.js.map} +0 -0
  90. /package/dist/{chunk-G6AVXG6F.js.map → chunk-LI5RF3H7.js.map} +0 -0
  91. /package/dist/{chunk-M7566JRS.js.map → chunk-NGQC5KIB.js.map} +0 -0
  92. /package/dist/{chunk-3UEZBTLV.js.map → chunk-OJPYOK4V.js.map} +0 -0
  93. /package/dist/{chunk-TU4FRZWU.js.map → chunk-PAJXMEHU.js.map} +0 -0
  94. /package/dist/{chunk-SOCKDRK6.js.map → chunk-RGR22IN6.js.map} +0 -0
  95. /package/dist/{chunk-KPYPJGSD.js.map → chunk-TPU5DT74.js.map} +0 -0
  96. /package/dist/{chunk-SCI6FKPM.js.map → chunk-TRZTLCL4.js.map} +0 -0
  97. /package/dist/{chunk-VF4ZV4KG.js.map → chunk-VJ2LZ2ZM.js.map} +0 -0
  98. /package/dist/{chunk-TJ6UFU3I.js.map → chunk-XXOE3OHH.js.map} +0 -0
  99. /package/dist/{cli-circuit-breaker-JWCKCVDX.js.map → cli-circuit-breaker-WKUYRX5M.js.map} +0 -0
  100. /package/dist/{composite-router-5OCKHBR3.js.map → composite-router-SKXMG4DA.js.map} +0 -0
  101. /package/dist/{consensus-vote-HG5BE56Y.js.map → consensus-vote-FGB637PZ.js.map} +0 -0
  102. /package/dist/{context-retriever-JNXUAPWJ.js.map → context-retriever-NK6O77TT.js.map} +0 -0
  103. /package/dist/{doctor-deep-IZAAFLBC.js.map → doctor-deep-EJAW6SI5.js.map} +0 -0
  104. /package/dist/{doctor-live-Z5IP2TWY.js.map → doctor-live-26YJA5Z4.js.map} +0 -0
  105. /package/dist/{expert-bridge-XI4BNDI7.js.map → expert-bridge-PNXWIDQV.js.map} +0 -0
  106. /package/dist/{factory-QAH46LJX.js.map → factory-KVIEFTAC.js.map} +0 -0
  107. /package/dist/{factory-WU2VY6QI.js.map → factory-MXWNULXD.js.map} +0 -0
  108. /package/dist/{improvement-review-E2PK2AEA.js.map → improvement-review-HKJNXMZM.js.map} +0 -0
  109. /package/dist/{init-opencode-BQHWMUOD.js.map → init-opencode-7IU4SWTC.js.map} +0 -0
  110. /package/dist/{issue-triage-6STMHG7L.js.map → issue-triage-EKFJQZFJ.js.map} +0 -0
  111. /package/dist/{pr-reviewer-helpers-NXG7X3WB.js.map → pr-reviewer-helpers-SUMMYYF7.js.map} +0 -0
  112. /package/dist/{registry-command-J2FV4BT2.js.map → registry-command-XFH3M634.js.map} +0 -0
  113. /package/dist/{repo-security-plan-IRILDORZ.js.map → repo-security-plan-RCYKT5GS.js.map} +0 -0
  114. /package/dist/{research-helpers-synthesize-KUZRNHXY.js.map → research-helpers-synthesize-BVJB4HW5.js.map} +0 -0
  115. /package/dist/{routing-memory-GFO76574.js.map → routing-memory-AR4YR2RL.js.map} +0 -0
  116. /package/dist/{session-memory-CY3T3XNO.js.map → session-memory-UYJU63EG.js.map} +0 -0
  117. /package/dist/{setup-command-GBCILO74.js.map → setup-command-LWHN672O.js.map} +0 -0
  118. /package/dist/{setup-config-A27SDCRY.js.map → setup-config-XZMHAOMR.js.map} +0 -0
  119. /package/dist/{setup-custom-api-UZV66PZA.js.map → setup-custom-api-BWX47BKD.js.map} +0 -0
  120. /package/dist/{tool-memory-YYEWKSC4.js.map → tool-memory-DB3YDR5W.js.map} +0 -0
  121. /package/dist/{unified-registry-PKDBZ6FO.js.map → unified-registry-XSY5XAIZ.js.map} +0 -0
  122. /package/dist/{weather-report-MXQTPLDG.js.map → weather-report-C2P6NHTX.js.map} +0 -0
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-SNJNRDKZ.js";
4
4
  import {
5
5
  createLogger
6
- } from "./chunk-6VUIQQ3O.js";
6
+ } from "./chunk-KTYTESEV.js";
7
7
  import "./chunk-2HUC6NZN.js";
8
8
  import "./chunk-OQLFRJCW.js";
9
9
  import "./chunk-ZPWHCABL.js";
@@ -62,4 +62,4 @@ export {
62
62
  generateMcpConfig,
63
63
  getDefaultAllowedTools
64
64
  };
65
- //# sourceMappingURL=child-mcp-config-MJNB5GXS.js.map
65
+ //# sourceMappingURL=child-mcp-config-5SWBEZPD.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getDefaultRegistry
3
- } from "./chunk-6VUIQQ3O.js";
3
+ } from "./chunk-KTYTESEV.js";
4
4
  import {
5
5
  getNexusDataDir
6
6
  } from "./chunk-OQLFRJCW.js";
@@ -128,4 +128,4 @@ export {
128
128
  loadUsageEvents,
129
129
  rollupByModel
130
130
  };
131
- //# sourceMappingURL=chunk-M3HKW5HF.js.map
131
+ //# sourceMappingURL=chunk-2ZVGB7AJ.js.map
@@ -3,7 +3,7 @@ import {
3
3
  createLogger,
4
4
  err,
5
5
  ok
6
- } from "./chunk-6VUIQQ3O.js";
6
+ } from "./chunk-KTYTESEV.js";
7
7
 
8
8
  // src/adapters/sdk/types.ts
9
9
  var PROVIDER_ENV_KEYS = {
@@ -169,4 +169,4 @@ export {
169
169
  validateCustomApiBaseUrl,
170
170
  assertCustomApiHostResolvesPublic
171
171
  };
172
- //# sourceMappingURL=chunk-ERCCVO4N.js.map
172
+ //# sourceMappingURL=chunk-3W7ST3AB.js.map
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  computeCostDetail,
3
3
  recordUsageEvent
4
- } from "./chunk-M3HKW5HF.js";
4
+ } from "./chunk-2ZVGB7AJ.js";
5
5
  import {
6
6
  BaseAdapter,
7
7
  createStream,
8
8
  planOptionalParams,
9
9
  requireApiKey,
10
10
  validateApiKeyPresence
11
- } from "./chunk-32AWWH4F.js";
11
+ } from "./chunk-5TE3ORD5.js";
12
12
  import {
13
13
  assertCustomApiHostResolvesPublic
14
- } from "./chunk-ERCCVO4N.js";
14
+ } from "./chunk-3W7ST3AB.js";
15
15
  import {
16
16
  ConfigError,
17
17
  ModelCapability,
@@ -22,7 +22,7 @@ import {
22
22
  getTimeProvider,
23
23
  getTokenEstimator,
24
24
  ok
25
- } from "./chunk-6VUIQQ3O.js";
25
+ } from "./chunk-KTYTESEV.js";
26
26
 
27
27
  // src/adapters/openai-types.ts
28
28
  var OPENAI_MODELS = {
@@ -831,4 +831,4 @@ export {
831
831
  discoverModels,
832
832
  buildOpenAICompatAdapters
833
833
  };
834
- //# sourceMappingURL=chunk-7FJFN74H.js.map
834
+ //# sourceMappingURL=chunk-5HGMO3W3.js.map
@@ -13,7 +13,7 @@ import {
13
13
  lookupInTreeCapability,
14
14
  ok,
15
15
  resolveRetryAfterMs
16
- } from "./chunk-6VUIQQ3O.js";
16
+ } from "./chunk-KTYTESEV.js";
17
17
 
18
18
  // src/config/model-parameter-support.ts
19
19
  function parseClaudeMajorMinor(bareId) {
@@ -834,4 +834,4 @@ export {
834
834
  DEFAULT_COLLECT_STREAM_MAX_CHUNKS,
835
835
  collectStream
836
836
  };
837
- //# sourceMappingURL=chunk-32AWWH4F.js.map
837
+ //# sourceMappingURL=chunk-5TE3ORD5.js.map
@@ -1,19 +1,19 @@
1
- import {
2
- resolveToken
3
- } from "./chunk-G6AVXG6F.js";
4
1
  import {
5
2
  REGISTRY_PATH,
6
3
  getProjectRoot,
7
4
  loadPapersRegistry,
8
5
  loadTechniquesRegistry,
9
6
  savePapersRegistry
10
- } from "./chunk-MAP3BVCT.js";
7
+ } from "./chunk-KHYSK52P.js";
11
8
  import {
12
9
  resolveInsideRoot
13
10
  } from "./chunk-NUBSJGQZ.js";
11
+ import {
12
+ resolveToken
13
+ } from "./chunk-LI5RF3H7.js";
14
14
  import {
15
15
  getToolMemory
16
- } from "./chunk-QUOCOEDQ.js";
16
+ } from "./chunk-C4BSQH3A.js";
17
17
  import {
18
18
  capitalizeWords,
19
19
  truncateSentence
@@ -38,7 +38,7 @@ import {
38
38
  getTimeProvider,
39
39
  ok,
40
40
  registerPersistentDistillerFactory
41
- } from "./chunk-6VUIQQ3O.js";
41
+ } from "./chunk-KTYTESEV.js";
42
42
  import {
43
43
  ensureLearningDir,
44
44
  getRulesFile
@@ -4090,4 +4090,4 @@ export {
4090
4090
  DEFAULT_CONTEXT_BUDGET_TOKENS,
4091
4091
  getContextPromptPrefix
4092
4092
  };
4093
- //# sourceMappingURL=chunk-JBLM3UZA.js.map
4093
+ //# sourceMappingURL=chunk-5XTQIBA3.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getRandomProvider,
3
3
  getTimeProvider
4
- } from "./chunk-6VUIQQ3O.js";
4
+ } from "./chunk-KTYTESEV.js";
5
5
 
6
6
  // src/utils/id-utils.ts
7
7
  import { randomUUID } from "crypto";
@@ -24,4 +24,4 @@ export {
24
24
  generateHyphenId,
25
25
  generateUUID
26
26
  };
27
- //# sourceMappingURL=chunk-N62DDWV3.js.map
27
+ //# sourceMappingURL=chunk-6GKG5U5Y.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  emitClawGuardViolation
3
- } from "./chunk-ZVZHNAQH.js";
3
+ } from "./chunk-BMLBXCC3.js";
4
4
  import {
5
5
  CACHE_TIMEOUTS,
6
6
  MCP_TIMEOUTS,
@@ -16,7 +16,7 @@ import {
16
16
  getTimeProvider,
17
17
  ok,
18
18
  resolveToolClassGuardMs
19
- } from "./chunk-6VUIQQ3O.js";
19
+ } from "./chunk-KTYTESEV.js";
20
20
  import {
21
21
  getNexusDataDir
22
22
  } from "./chunk-OQLFRJCW.js";
@@ -2028,4 +2028,4 @@ export {
2028
2028
  VOTING_THRESHOLDS,
2029
2029
  ConsensusMetricsSchema
2030
2030
  };
2031
- //# sourceMappingURL=chunk-K3TRG2CD.js.map
2031
+ //# sourceMappingURL=chunk-7WIBQZR6.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  computeCostDetail,
3
3
  priceBasisOf
4
- } from "./chunk-M3HKW5HF.js";
4
+ } from "./chunk-2ZVGB7AJ.js";
5
5
  import {
6
6
  allOf
7
7
  } from "./chunk-OQB3XC5O.js";
@@ -22,20 +22,20 @@ import {
22
22
  toolSuccess,
23
23
  withProgressHeartbeat,
24
24
  wrapToolWithTimeout
25
- } from "./chunk-K3TRG2CD.js";
25
+ } from "./chunk-7WIBQZR6.js";
26
26
  import {
27
27
  createEvent,
28
28
  getGlobalRegistry
29
- } from "./chunk-SOCKDRK6.js";
29
+ } from "./chunk-RGR22IN6.js";
30
30
  import {
31
31
  getToolMemory
32
- } from "./chunk-QUOCOEDQ.js";
32
+ } from "./chunk-C4BSQH3A.js";
33
33
  import {
34
34
  authRemediation,
35
35
  delay,
36
36
  getAvailableClis,
37
37
  withTimeout
38
- } from "./chunk-M7566JRS.js";
38
+ } from "./chunk-NGQC5KIB.js";
39
39
  import {
40
40
  AgentError,
41
41
  CLI_NAMES,
@@ -64,7 +64,7 @@ import {
64
64
  resolveClassGuardMs,
65
65
  resolveModelIdentitySync,
66
66
  resolveVoteTimeout
67
- } from "./chunk-6VUIQQ3O.js";
67
+ } from "./chunk-KTYTESEV.js";
68
68
  import {
69
69
  ensureLearningDir,
70
70
  getOutcomesFile
@@ -6190,7 +6190,7 @@ async function processVotesWithCascade(engineVotes, opts) {
6190
6190
  var CONTRARIAN_ESCALATION_THRESHOLD = 0.8;
6191
6191
  async function runContrarianCheck(proposal, log) {
6192
6192
  try {
6193
- const { executeExpert } = await import("./expert-bridge-XI4BNDI7.js");
6193
+ const { executeExpert } = await import("./expert-bridge-PNXWIDQV.js");
6194
6194
  const prompt = [
6195
6195
  "You are a contrarian analyst. Your job is to find reasons this proposal should be REJECTED.",
6196
6196
  "Look for: YAGNI (not needed), MISALIGNED (wrong tech/architecture), SECURITY_RISK, SCOPE_CREEP.",
@@ -6756,4 +6756,4 @@ export {
6756
6756
  CONSENSUS_VOTE_TOOL_SCHEMA,
6757
6757
  registerConsensusVoteTool
6758
6758
  };
6759
- //# sourceMappingURL=chunk-DMKV7ANB.js.map
6759
+ //# sourceMappingURL=chunk-ABU3SZXU.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  loadPapersRegistry
3
- } from "./chunk-MAP3BVCT.js";
3
+ } from "./chunk-KHYSK52P.js";
4
4
  import {
5
5
  getErrorMessage
6
- } from "./chunk-6VUIQQ3O.js";
6
+ } from "./chunk-KTYTESEV.js";
7
7
 
8
8
  // src/cli/research-helpers-synthesize.ts
9
9
  import { z } from "zod";
@@ -876,4 +876,4 @@ export {
876
876
  AttributedInsightSchema,
877
877
  synthesizeResearch
878
878
  };
879
- //# sourceMappingURL=chunk-JBSL2HKV.js.map
879
+ //# sourceMappingURL=chunk-AV3NVATT.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getTimeProvider
3
- } from "./chunk-6VUIQQ3O.js";
3
+ } from "./chunk-KTYTESEV.js";
4
4
 
5
5
  // src/security/audit-trail.ts
6
6
  var MAX_EVENTS = 1e4;
@@ -205,4 +205,4 @@ export {
205
205
  createGraphAuditBridge,
206
206
  createAuditTrail
207
207
  };
208
- //# sourceMappingURL=chunk-ZVZHNAQH.js.map
208
+ //# sourceMappingURL=chunk-BMLBXCC3.js.map
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-6T3EPABN.js";
6
6
  import {
7
7
  SessionMemory
8
- } from "./chunk-OL6TRWBA.js";
8
+ } from "./chunk-JHL7JLRJ.js";
9
9
  import {
10
10
  StatsOnlyAdapter,
11
11
  ensureSharedMemoryRegistry
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-KSQSWEFH.js";
16
16
  import {
17
17
  generateId
18
- } from "./chunk-N62DDWV3.js";
18
+ } from "./chunk-6GKG5U5Y.js";
19
19
  import {
20
20
  ErrorCode,
21
21
  NexusError,
@@ -28,7 +28,7 @@ import {
28
28
  getTimeProvider,
29
29
  ok,
30
30
  setSharedMobiMemDbPathResolver
31
- } from "./chunk-6VUIQQ3O.js";
31
+ } from "./chunk-KTYTESEV.js";
32
32
  import {
33
33
  nexusDataPath
34
34
  } from "./chunk-OQLFRJCW.js";
@@ -5552,4 +5552,4 @@ export {
5552
5552
  reinitializeMemoryBackends,
5553
5553
  ToolMemoryManager
5554
5554
  };
5555
- //# sourceMappingURL=chunk-QUOCOEDQ.js.map
5555
+ //# sourceMappingURL=chunk-C4BSQH3A.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createLogger,
6
6
  getTimeProvider
7
- } from "./chunk-6VUIQQ3O.js";
7
+ } from "./chunk-KTYTESEV.js";
8
8
 
9
9
  // src/mcp/tools/scanner-registry-fetcher.ts
10
10
  import { z } from "zod";
@@ -721,4 +721,4 @@ export {
721
721
  generateSecurityPlan,
722
722
  buildPlanFromAnalysis
723
723
  };
724
- //# sourceMappingURL=chunk-MXJEMYC7.js.map
724
+ //# sourceMappingURL=chunk-CDWKQ5FV.js.map
@@ -2,7 +2,7 @@ import {
2
2
  CACHE_TIMEOUTS,
3
3
  createLogger,
4
4
  getTimeProvider
5
- } from "./chunk-6VUIQQ3O.js";
5
+ } from "./chunk-KTYTESEV.js";
6
6
 
7
7
  // src/security/trust-types.ts
8
8
  import { z } from "zod";
@@ -483,7 +483,7 @@ function determineEffectiveTier(userRole, signals) {
483
483
  if (signals.some((s) => hostileSignals.includes(s))) return "4";
484
484
  if (signals.length >= 2) {
485
485
  const baseNumeric = TRUST_TIER_NUMERIC[baseTier];
486
- const downgraded = Math.min(baseNumeric + 1, 4);
486
+ const downgraded = Math.min(baseNumeric + 1, 3);
487
487
  return String(downgraded);
488
488
  }
489
489
  return baseTier;
@@ -560,4 +560,4 @@ export {
560
560
  resolveReputationGatingMode,
561
561
  gateWithReputation
562
562
  };
563
- //# sourceMappingURL=chunk-X2HMEG5F.js.map
563
+ //# sourceMappingURL=chunk-CODRIZMT.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/security/trust-types.ts","../src/security/input-sanitizer.ts","../src/security/reputation-model.ts","../src/security/env-mode.ts"],"sourcesContent":["/**\n * nexus-agents/security - Trust Types\n *\n * Zod schemas and TypeScript types for the untrusted input hardening\n * framework. Defines trust tiers, sanitized input, user roles, and\n * injection detection flags.\n *\n * @module security/trust-types\n * (Source: Issue #818, #819 — Phase 1: Input Sanitization)\n */\n\nimport { z } from 'zod';\n\n// ============================================================================\n// Trust Tiers\n// ============================================================================\n\n/**\n * Trust tier classification for input sources.\n * Lower number = higher trust.\n *\n * 1 = Authoritative (repo files, CI, CLAUDE.md, allowlisted maintainers)\n * 2 = Semi-trusted (collaborator issue body, contributor PR metadata)\n * 3 = Untrusted (unknown user comments, non-collaborator issue body)\n * 4 = Hostile (injection patterns, hidden HTML, instruction-like content)\n */\nexport const TrustTierSchema = z.enum(['1', '2', '3', '4']);\nexport type TrustTier = z.infer<typeof TrustTierSchema>;\n\n/** Numeric trust tier for comparisons. Higher number = lower trust. */\nexport const TRUST_TIER_NUMERIC: Record<TrustTier, number> = {\n '1': 1,\n '2': 2,\n '3': 3,\n '4': 4,\n};\n\n// ============================================================================\n// GitHub User Roles\n// ============================================================================\n\n/**\n * GitHub user relationship to the repository.\n */\nexport const GitHubUserRoleSchema = z.enum([\n 'owner',\n 'maintainer',\n 'collaborator',\n 'contributor',\n 'member',\n 'unknown',\n]);\nexport type GitHubUserRole = z.infer<typeof GitHubUserRoleSchema>;\n\n/**\n * Default trust tier mapping for each GitHub role.\n * Can be overridden by injection pattern detection (downgrade only).\n */\nexport const ROLE_DEFAULT_TRUST: Record<GitHubUserRole, TrustTier> = {\n owner: '1',\n maintainer: '1',\n collaborator: '2',\n contributor: '2',\n member: '3',\n unknown: '3',\n};\n\n// ============================================================================\n// Injection Detection\n// ============================================================================\n\n/**\n * Categories of injection patterns detected in content.\n */\nexport const InjectionFlagSchema = z.enum([\n 'authority_claim',\n 'instruction_pattern',\n 'system_prompt_manipulation',\n 'hidden_content',\n 'urgency_manipulation',\n 'fake_conversation',\n 'base64_encoded',\n 'external_link_instruction',\n]);\nexport type InjectionFlag = z.infer<typeof InjectionFlagSchema>;\n\n/**\n * An element stripped during sanitization, preserved for audit trail.\n */\nexport const StrippedElementSchema = z.object({\n /** Type of element stripped. */\n tag: z.string().min(1),\n /** Reason for stripping. */\n reason: z.string().min(1),\n /** Start index in original content. */\n startIndex: z.number().int().nonnegative(),\n /** Length of stripped content. */\n length: z.number().int().positive(),\n});\nexport type StrippedElement = z.infer<typeof StrippedElementSchema>;\n\n// ============================================================================\n// Sanitized Input\n// ============================================================================\n\n/**\n * The result of sanitizing untrusted input.\n * Contains cleaned content, trust classification, and audit data.\n */\nexport const SanitizedInputSchema = z.object({\n /** Sanitized content with dangerous elements removed. */\n content: z.string(),\n /** Original content before sanitization (for audit). */\n originalLength: z.number().int().nonnegative(),\n /** Assigned trust tier based on user role and content analysis. */\n trustTier: TrustTierSchema,\n /** GitHub user role of the input source. */\n userRole: GitHubUserRoleSchema,\n /** Injection patterns detected in content. */\n injectionFlags: z.array(InjectionFlagSchema),\n /** Elements stripped during sanitization (audit trail). */\n strippedElements: z.array(StrippedElementSchema),\n /** Whether any dangerous content was detected and stripped. */\n wasModified: z.boolean(),\n /** Timestamp of sanitization (ISO 8601). */\n sanitizedAt: z.iso.datetime(),\n});\nexport type SanitizedInput = z.infer<typeof SanitizedInputSchema>;\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/**\n * Configuration for the input sanitizer.\n */\nexport const SanitizerConfigSchema = z.object({\n /** GitHub usernames that are always Tier 1 (allowlisted maintainers). */\n allowlistedMaintainers: z.array(z.string().min(1)).default([]),\n /** Whether to fail open (log only) or fail closed (block). Phase 1 = open. */\n failOpen: z.boolean().default(true),\n /** Maximum input length before truncation. */\n maxInputLength: z.number().int().positive().default(50_000),\n});\nexport type SanitizerConfig = z.infer<typeof SanitizerConfigSchema>;\n","/**\n * nexus-agents/security - Input Sanitizer\n *\n * Sanitizes untrusted GitHub input by stripping dangerous HTML/XML tags,\n * detecting injection patterns, and producing a SanitizedInput result\n * with full audit trail.\n *\n * Defense layer 1 of the three-layer hardening architecture.\n * See: docs/architecture/UNTRUSTED_INPUT_HARDENING.md\n *\n * @module security/input-sanitizer\n * (Source: Issue #818, #819 — Phase 1: Input Sanitization)\n */\n\nimport type {\n InjectionFlag,\n SanitizedInput,\n SanitizerConfig,\n StrippedElement,\n TrustTier,\n GitHubUserRole,\n} from './trust-types.js';\nimport { ROLE_DEFAULT_TRUST, SanitizerConfigSchema } from './trust-types.js';\n\n// ============================================================================\n// Dangerous HTML Patterns (Trail of Bits / GitHub Copilot vectors)\n// ============================================================================\n\n/**\n * HTML tags to strip. These are known injection vectors:\n * - `<picture>` / `<source>`: Trail of Bits GitHub Copilot injection\n * - `<img>`: Can carry injection via alt text or onerror\n */\nconst DANGEROUS_HTML_PATTERN =\n /<(picture|source|img)\\b[^>]*>[\\s\\S]*?<\\/\\1>|<(picture|source|img)\\b[^>]*\\/?>/gi;\n\n// ============================================================================\n// XML-like Tags (Conversation History Injection)\n// ============================================================================\n\n/**\n * XML-like tags that mimic conversation structure or system prompts.\n */\nconst XML_INJECTION_PATTERN =\n /<\\/?(system|human|assistant|instructions|user|prompt|context|tool_use|tool_result)\\b[^>]*>/gi;\n\n// ============================================================================\n// HTML Comments with Instructions\n// ============================================================================\n\nconst HTML_COMMENT_PATTERN = /<!--[\\s\\S]*?-->/g;\n\n// ============================================================================\n// Injection Pattern Detectors\n// ============================================================================\n\ninterface PatternMatch {\n flag: InjectionFlag;\n pattern: RegExp;\n}\n\nconst INJECTION_PATTERNS: readonly PatternMatch[] = [\n {\n flag: 'authority_claim',\n pattern: /\\b(as (?:a|the) (?:maintainer|admin|owner|security lead|repo owner|developer))\\b/i,\n },\n {\n flag: 'authority_claim',\n pattern: /\\b(i(?:'m| am) the (?:repo |project )?(?:owner|maintainer|admin))\\b/i,\n },\n {\n flag: 'instruction_pattern',\n pattern: /\\b(please (?:close|merge|label|mark|apply|delete|remove|approve|reject))\\b/i,\n },\n {\n flag: 'instruction_pattern',\n pattern: /\\b(you (?:should|must|need to) (?:close|merge|label|apply|delete))\\b/i,\n },\n {\n flag: 'system_prompt_manipulation',\n pattern: /\\b(ignore (?:all )?previous (?:instructions|rules|prompts))\\b/i,\n },\n {\n flag: 'system_prompt_manipulation',\n pattern: /\\b(forget (?:your |all )?(?:instructions|rules|safety))\\b/i,\n },\n {\n flag: 'system_prompt_manipulation',\n pattern: /\\b(new (?:instructions|rules|system prompt|directives))\\b/i,\n },\n {\n flag: 'urgency_manipulation',\n pattern: /\\b(critical|emergency|urgent|must act now|immediately|time[- ]?sensitive)\\b/i,\n },\n {\n flag: 'fake_conversation',\n pattern: /<(?:assistant|human|user|system)>/i,\n },\n // base64_encoded is detected separately by `looksLikeBase64Payload` below.\n // The lookahead-based regex it replaced exhibited catastrophic backtracking\n // on long hex-only inputs (#2191) — see `looksLikeBase64Payload` for the\n // two-phase rewrite that preserves the #1811 SHA-hash false-positive guard.\n {\n flag: 'external_link_instruction',\n pattern: /(?:apply|run|execute|install)\\s+(?:this\\s+)?(?:from\\s+)?https?:\\/\\//i,\n },\n];\n\n/**\n * Two-phase base64-payload detection (#2191).\n *\n * The original `(?=[A-Za-z0-9+/]*[g-zG-Z+/=])[A-Za-z0-9+/]{40,}={0,2}` regex\n * was vulnerable to catastrophic backtracking — V8 took ~1.8s on a 50K\n * hex-only adversarial input. Splitting the check into two non-overlapping\n * phases removes the lookahead and makes the worst case linear.\n *\n * Phase 1: find a 40+ run of base64-alphabet chars (no lookahead).\n * Phase 2: confirm the matched substring contains a base64-discriminating\n * char (g-z, G-Z, +, /, =) so SHA-1 / SHA-256 hex hashes don't\n * false-positive (preserves #1811 behavior).\n *\n * Same detection coverage as the original; same false-positive resistance.\n */\nconst BASE64_RUN_GLOBAL = /[A-Za-z0-9+/]{40,}={0,2}/g;\nconst BASE64_DISCRIMINATOR = /[g-zG-Z+/=]/;\n\nfunction looksLikeBase64Payload(content: string): boolean {\n // Iterate every 40+ base64-alphabet run rather than just the first one,\n // so a long hex-only prefix doesn't mask a real base64 payload that\n // appears later in the content.\n for (const match of content.matchAll(BASE64_RUN_GLOBAL)) {\n if (BASE64_DISCRIMINATOR.test(match[0])) return true;\n }\n return false;\n}\n\n// ============================================================================\n// HTML Entity Decoding (evasion defense)\n// ============================================================================\n\n/**\n * Dangerous-tag names we will still match after entity decoding.\n * Kept in sync with DANGEROUS_HTML_PATTERN and XML_INJECTION_PATTERN above.\n */\nconst DANGEROUS_TAG_NAMES =\n 'picture|source|img|system|human|assistant|instructions|user|prompt|context|tool_use|tool_result';\n\n/**\n * Detects whether the input contains entity-encoded forms of any dangerous\n * tag (&lt;picture, &#60;system, &#x3c;img …). Used as a cheap pre-check so\n * that benign content with legitimate entities (e.g. \"AT&amp;T\") is passed\n * through untouched and wasModified stays false.\n */\nconst ENCODED_DANGEROUS_TAG_PATTERN = new RegExp(\n `&(?:lt|#0*60|#x0*3c);\\\\s*\\\\/?\\\\s*(?:${DANGEROUS_TAG_NAMES})\\\\b`,\n 'i'\n);\n\n/** Decodes the subset of HTML entities that can reconstruct tag syntax. */\nfunction decodeEntities(content: string): string {\n return (\n content\n // Numeric (decimal) references\n .replace(/&#(\\d+);/g, (_match, dec: string) => {\n const code = Number.parseInt(dec, 10);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _match;\n })\n // Numeric (hex) references\n .replace(/&#x([0-9a-f]+);/gi, (_match, hex: string) => {\n const code = Number.parseInt(hex, 16);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _match;\n })\n // Named entities most relevant to tag reconstruction\n .replace(/&lt;/gi, '<')\n .replace(/&gt;/gi, '>')\n .replace(/&quot;/gi, '\"')\n .replace(/&apos;/gi, \"'\")\n // &amp; must be decoded last so that &amp;lt; does not resurface as <\n .replace(/&amp;/gi, '&')\n );\n}\n\n/**\n * Runs decodeEntities only if the input contains an entity-encoded dangerous\n * tag. This keeps benign content with legitimate entities untouched.\n */\nfunction applyEntityEvasionDefense(content: string): {\n cleaned: string;\n stripped: StrippedElement[];\n} {\n if (!ENCODED_DANGEROUS_TAG_PATTERN.test(content)) {\n return { cleaned: content, stripped: [] };\n }\n const decoded = decodeEntities(content);\n return {\n cleaned: decoded,\n stripped: [\n {\n tag: '&…;',\n reason: 'HTML entity-encoded dangerous tag decoded for stripping (CWE-79)',\n startIndex: 0,\n length: content.length,\n },\n ],\n };\n}\n\n// ============================================================================\n// Core Sanitization Functions\n// ============================================================================\n\n/** Strips dangerous HTML tags and records what was removed.\n * Loops until stable to prevent reconstructed patterns after removal (#1496). */\nfunction stripDangerousHtml(content: string): {\n cleaned: string;\n stripped: StrippedElement[];\n} {\n const stripped: StrippedElement[] = [];\n let cleaned = content;\n const MAX_PASSES = 5;\n for (let pass = 0; pass < MAX_PASSES; pass++) {\n DANGEROUS_HTML_PATTERN.lastIndex = 0;\n if (!DANGEROUS_HTML_PATTERN.test(cleaned)) break;\n cleaned = cleaned.replace(DANGEROUS_HTML_PATTERN, (match, _g1, _g2, offset: number) => {\n stripped.push({\n tag: match.slice(0, 30) + (match.length > 30 ? '...' : ''),\n reason: 'Dangerous HTML tag (Trail of Bits injection vector)',\n startIndex: offset,\n length: match.length,\n });\n return '';\n });\n }\n return { cleaned, stripped };\n}\n\n/** Strips XML-like tags that mimic conversation structure.\n * Loops until stable to prevent reconstructed patterns after removal (#1496). */\nfunction stripXmlTags(content: string): {\n cleaned: string;\n stripped: StrippedElement[];\n} {\n const stripped: StrippedElement[] = [];\n let cleaned = content;\n const MAX_PASSES = 5;\n for (let pass = 0; pass < MAX_PASSES; pass++) {\n XML_INJECTION_PATTERN.lastIndex = 0;\n if (!XML_INJECTION_PATTERN.test(cleaned)) break;\n cleaned = cleaned.replace(XML_INJECTION_PATTERN, (match, _g1, offset: number) => {\n stripped.push({\n tag: match,\n reason: 'XML-like conversation injection tag',\n startIndex: offset,\n length: match.length,\n });\n return '';\n });\n }\n return { cleaned, stripped };\n}\n\n/** Strips HTML comments that may contain hidden instructions.\n * Loops until stable to prevent reconstructed comment patterns (#1496). */\nfunction stripHtmlComments(content: string): {\n cleaned: string;\n stripped: StrippedElement[];\n} {\n const stripped: StrippedElement[] = [];\n let cleaned = content;\n // Loop until stable: stripping may reveal new instruction-bearing comments\n const MAX_PASSES = 5;\n for (let pass = 0; pass < MAX_PASSES; pass++) {\n HTML_COMMENT_PATTERN.lastIndex = 0;\n const prevLength = cleaned.length;\n cleaned = cleaned.replace(HTML_COMMENT_PATTERN, (match, offset: number) => {\n const hasInstruction = /\\b(ignore|execute|close|merge|delete|apply)\\b/i.test(match);\n if (!hasInstruction) return match;\n\n stripped.push({\n tag: '<!-- ... -->',\n reason: 'HTML comment with instruction-like content',\n startIndex: offset,\n length: match.length,\n });\n return '';\n });\n if (cleaned.length === prevLength) break;\n }\n // Second pass: strip unclosed <!-- tags (incomplete comment injection)\n let searchFrom = 0;\n while (searchFrom < cleaned.length) {\n const openIdx = cleaned.indexOf('<!--', searchFrom);\n if (openIdx === -1) break;\n const closeIdx = cleaned.indexOf('-->', openIdx + 4);\n if (closeIdx === -1) {\n stripped.push({\n tag: '<!--',\n reason: 'Unclosed HTML comment (potential injection vector)',\n startIndex: openIdx,\n length: cleaned.length - openIdx,\n });\n cleaned = cleaned.slice(0, openIdx);\n break;\n }\n searchFrom = closeIdx + 3;\n }\n return { cleaned, stripped };\n}\n\n/** Detects injection patterns in content without modifying it. */\nfunction detectInjectionPatterns(content: string): InjectionFlag[] {\n const flags = new Set<InjectionFlag>();\n for (const { flag, pattern } of INJECTION_PATTERNS) {\n // Reset lastIndex for global patterns\n pattern.lastIndex = 0;\n if (pattern.test(content)) {\n flags.add(flag);\n }\n }\n if (looksLikeBase64Payload(content)) {\n flags.add('base64_encoded');\n }\n return Array.from(flags);\n}\n\n/** Lower-cases an arbitrary role literal and maps it to a known role. */\nfunction normalizeRole(userRole: string): GitHubUserRole {\n const lower = userRole.toLowerCase();\n if (lower in ROLE_DEFAULT_TRUST) return lower as GitHubUserRole;\n return 'unknown';\n}\n\n/**\n * Assigns trust tier based on user role and injection analysis.\n * Injection patterns can only DOWNGRADE trust, never upgrade.\n */\nfunction assignTrustTier(\n userRole: GitHubUserRole,\n injectionFlags: readonly InjectionFlag[],\n allowlisted: boolean\n): TrustTier {\n if (allowlisted) return '1';\n\n // Defensive lowercase — the type says GitHubUserRole, but callers\n // sometimes cast an unnormalized GitHub author_association literal\n // (e.g. 'OWNER', 'MEMBER') directly. Normalize here so the maintainer\n // exemption below matches regardless of case (CWE-178).\n const normalizedRole = normalizeRole(userRole);\n const baseTier = ROLE_DEFAULT_TRUST[normalizedRole];\n\n // Content with injection patterns is downgraded to Tier 4 (hostile)\n const hostileFlags: InjectionFlag[] = ['system_prompt_manipulation', 'fake_conversation'];\n if (injectionFlags.some((f) => hostileFlags.includes(f))) return '4';\n\n // Content with authority claims from non-maintainers is suspicious\n if (\n injectionFlags.includes('authority_claim') &&\n normalizedRole !== 'owner' &&\n normalizedRole !== 'maintainer'\n ) {\n return '4';\n }\n\n return baseTier;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Sanitizes untrusted GitHub input through the full Layer 1 pipeline:\n * 1. HTML stripping (picture/source/img tags)\n * 2. XML tag stripping (system/human/assistant)\n * 3. HTML comment stripping (instruction-bearing comments only)\n * 4. Injection pattern detection\n * 5. Trust tier assignment\n *\n * ⚠ **Use HostileInputFirewall.process() in agent code paths.** Calling\n * sanitizeInput() directly only runs Layer 1 — it does not evaluate the\n * Rule of Two (enforced in policy-gate.ts via evaluatePolicy) and does\n * not emit audit-trail events. An agent that processes untrusted input\n * while holding both write access and secrets violates the Rule of Two;\n * the policy gate is what catches this, and it only runs inside the\n * firewall pipeline. Direct use of this function is appropriate for\n * unit tests and pure content analysis, not for agent decision paths.\n *\n * @see packages/nexus-agents/src/security/firewall/firewall-pipeline.ts\n * @see packages/nexus-agents/src/security/policy-gate.ts\n * @param content - Raw untrusted content from GitHub\n * @param userRole - GitHub user's relationship to the repository\n * @param username - GitHub username (for allowlist check)\n * @param config - Optional sanitizer configuration\n * @returns SanitizedInput with cleaned content and audit data\n */\nexport function sanitizeInput(\n content: string,\n userRole: GitHubUserRole,\n username: string,\n config?: Partial<SanitizerConfig>\n): SanitizedInput {\n const cfg = SanitizerConfigSchema.parse(config ?? {});\n const truncated = content.slice(0, cfg.maxInputLength);\n const allowlisted = cfg.allowlistedMaintainers.includes(username);\n\n try {\n // Pipeline: decode entity-encoded dangerous tags, then strip dangerous content\n const entityDecoded = applyEntityEvasionDefense(truncated);\n const html = stripDangerousHtml(entityDecoded.cleaned);\n const xml = stripXmlTags(html.cleaned);\n const comments = stripHtmlComments(xml.cleaned);\n const allStripped = [\n ...entityDecoded.stripped,\n ...html.stripped,\n ...xml.stripped,\n ...comments.stripped,\n ];\n\n // Detect injection patterns on ORIGINAL content (before stripping)\n const injectionFlags = detectInjectionPatterns(truncated);\n\n // Assign trust tier\n const trustTier = assignTrustTier(userRole, injectionFlags, allowlisted);\n\n return {\n content: comments.cleaned,\n originalLength: content.length,\n trustTier,\n userRole,\n injectionFlags,\n strippedElements: allStripped,\n wasModified: allStripped.length > 0,\n sanitizedAt: new Date().toISOString(),\n };\n } catch (err: unknown) {\n return buildFailClosedResult(err, content, truncated, userRole, allowlisted);\n }\n}\n\n/**\n * Fail-closed result returned when the sanitizer pipeline throws.\n * Returns empty content at Tier-4 (or Tier-1 for allowlisted maintainers,\n * since their bypass is not regex-dependent) so downstream consumers\n * cannot act on untrusted content we could not validate.\n * CLAUDE.md: \"Fail closed on ambiguity.\"\n */\nfunction buildFailClosedResult(\n err: unknown,\n originalContent: string,\n truncated: string,\n userRole: GitHubUserRole,\n allowlisted: boolean\n): SanitizedInput {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: '',\n originalLength: originalContent.length,\n trustTier: allowlisted ? '1' : '4',\n userRole,\n injectionFlags: [],\n strippedElements: [\n {\n tag: '(pipeline-failure)',\n reason: `Sanitizer pipeline threw; input discarded as fail-closed: ${message}`,\n startIndex: 0,\n length: truncated.length,\n },\n ],\n wasModified: true,\n sanitizedAt: new Date().toISOString(),\n };\n}\n","/**\n * nexus-agents/security - Reputation Model\n *\n * Lightweight trust model for GitHub users that assesses reputation\n * based on account age, contribution history, and behavioral signals.\n * Integrates with the trust classifier for comprehensive trust assessment.\n *\n * @module security/reputation-model\n * (Source: Issue #818, #824 — Phase 3: Reputation Model)\n */\n\nimport { z } from 'zod';\n\nimport { getTimeProvider } from '../core/index.js';\nimport { resolveEnvMode } from './env-mode.js';\nimport type { TrustTier, GitHubUserRole, InjectionFlag } from './trust-types.js';\nimport { TRUST_TIER_NUMERIC, ROLE_DEFAULT_TRUST } from './trust-types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Signals that indicate a suspicious actor.\n */\nexport const SuspiciousSignalSchema = z.enum([\n 'new_account',\n 'no_prior_contributions',\n 'injection_patterns_detected',\n 'rapid_comments',\n 'mismatched_authority_claim',\n]);\nexport type SuspiciousSignal = z.infer<typeof SuspiciousSignalSchema>;\n\n/**\n * GitHub user metadata for reputation assessment.\n */\nexport interface GitHubUserMetadata {\n readonly username: string;\n /**\n * Account/activity fields are OPTIONAL (#3106). When a field is absent (the\n * caller couldn't fetch it — e.g. the firewall before Phase 3 wiring), its\n * signal is SKIPPED rather than fabricated: an unknown value must never be\n * treated as benign (the old hardcoded `365`/`0`) nor as hostile. Only the\n * `authorAssociation` + `injectionFlags` signals fire on absent activity data.\n */\n readonly accountAgeDays?: number;\n readonly priorContributions?: number;\n readonly recentCommentCount?: number;\n readonly recentCommentWindowMinutes?: number;\n readonly authorAssociation: string;\n readonly injectionFlags: readonly InjectionFlag[];\n}\n\n/**\n * Result of a reputation assessment.\n */\nexport interface ReputationAssessment {\n readonly username: string;\n readonly userRole: GitHubUserRole;\n readonly suspiciousSignals: readonly SuspiciousSignal[];\n readonly isSuspicious: boolean;\n readonly effectiveTrustTier: TrustTier;\n readonly reputationScore: number;\n readonly reason: string;\n readonly assessedAt: string;\n}\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/** Thresholds for suspicious behavior detection. */\n/** Approximate days per year, used to normalize account age to a 0–10 scale. */\nconst DAYS_PER_YEAR_APPROX = 36.5;\n\nconst SUSPICIOUS_THRESHOLDS = {\n /** Account younger than this (days) is flagged. */\n newAccountDays: 30,\n /** Fewer contributions than this is flagged. */\n minContributions: 1,\n /** More comments than this in the window triggers rapid-comment flag. */\n rapidCommentThreshold: 5,\n /** Time window (minutes) for rapid comment detection. */\n rapidCommentWindowMinutes: 10,\n} as const;\n\n// ============================================================================\n// Reputation Cache\n// ============================================================================\n\ninterface CacheEntry {\n assessment: ReputationAssessment;\n expiresAt: number;\n}\n\n// Canonical source: config/timeouts.ts (Issue #1046)\nimport { CACHE_TIMEOUTS } from '../config/timeouts.js';\n\nconst DEFAULT_TTL_MS: number = CACHE_TIMEOUTS.reputationTtlMs;\nconst DEFAULT_MAX_SIZE = 1000;\n\n/**\n * In-memory reputation cache with TTL and max size.\n * Reduces redundant assessments for the same user within a short window.\n * Evicts oldest entries when max size is exceeded.\n */\nexport class ReputationCache {\n private readonly cache = new Map<string, CacheEntry>();\n private readonly ttlMs: number;\n private readonly maxSize: number;\n\n constructor(ttlMs = DEFAULT_TTL_MS, maxSize = DEFAULT_MAX_SIZE) {\n this.ttlMs = ttlMs;\n this.maxSize = maxSize;\n }\n\n get(username: string): ReputationAssessment | undefined {\n const entry = this.cache.get(username);\n if (entry === undefined) return undefined;\n if (getTimeProvider().now() > entry.expiresAt) {\n this.cache.delete(username);\n return undefined;\n }\n return entry.assessment;\n }\n\n set(username: string, assessment: ReputationAssessment): void {\n if (this.cache.size >= this.maxSize && !this.cache.has(username)) {\n this.evictOldest();\n }\n this.cache.set(username, {\n assessment,\n expiresAt: getTimeProvider().now() + this.ttlMs,\n });\n }\n\n /** Evict a batch of oldest entries (10% of maxSize, minimum 1). */\n private evictOldest(): void {\n const batchSize = Math.max(1, Math.floor(this.maxSize * 0.1));\n const keys = this.cache.keys();\n for (let i = 0; i < batchSize; i++) {\n const next = keys.next();\n if (next.done === true) break;\n this.cache.delete(next.value);\n }\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n\n// ============================================================================\n// Suspicious Signal Detection\n// ============================================================================\n\n/** Only hostile-tier injection flags count — benign flags like\n * instruction_pattern (\"please remove\") must not trip the injection signal. */\nconst HOSTILE_INJECTION_FLAGS: readonly InjectionFlag[] = [\n 'system_prompt_manipulation',\n 'fake_conversation',\n 'authority_claim',\n 'hidden_content',\n];\n\nfunction hasHostileInjection(flags: readonly InjectionFlag[]): boolean {\n return flags.some((f) => HOSTILE_INJECTION_FLAGS.includes(f));\n}\n\n/** Rapid-comment burst — only when both count and window are known (#3106). */\nfunction isRapidCommenting(m: GitHubUserMetadata): boolean {\n return (\n m.recentCommentCount !== undefined &&\n m.recentCommentWindowMinutes !== undefined &&\n m.recentCommentCount > SUSPICIOUS_THRESHOLDS.rapidCommentThreshold &&\n m.recentCommentWindowMinutes <= SUSPICIOUS_THRESHOLDS.rapidCommentWindowMinutes\n );\n}\n\n/** Authority claim from a non-maintainer role. */\nfunction isMismatchedAuthority(m: GitHubUserMetadata): boolean {\n if (!m.injectionFlags.includes('authority_claim')) return false;\n const association = m.authorAssociation.toUpperCase();\n return association !== 'OWNER' && association !== 'MEMBER';\n}\n\n/** Detect all suspicious signals from user metadata. #3106: account/activity\n * signals are skipped when their data is absent — never fabricated. */\nfunction detectSuspiciousSignals(metadata: GitHubUserMetadata): SuspiciousSignal[] {\n const signals: SuspiciousSignal[] = [];\n const { accountAgeDays, priorContributions } = metadata;\n\n if (accountAgeDays !== undefined && accountAgeDays < SUSPICIOUS_THRESHOLDS.newAccountDays) {\n signals.push('new_account');\n }\n if (\n priorContributions !== undefined &&\n priorContributions < SUSPICIOUS_THRESHOLDS.minContributions\n ) {\n signals.push('no_prior_contributions');\n }\n if (hasHostileInjection(metadata.injectionFlags)) signals.push('injection_patterns_detected');\n if (isRapidCommenting(metadata)) signals.push('rapid_comments');\n if (isMismatchedAuthority(metadata)) signals.push('mismatched_authority_claim');\n\n return signals;\n}\n\n/** Calculate a 0-100 reputation score. */\nfunction calculateReputationScore(\n metadata: GitHubUserMetadata,\n signals: readonly SuspiciousSignal[],\n userRole: GitHubUserRole\n): number {\n let score = 50; // baseline\n\n // Role bonus\n const roleBonus: Record<GitHubUserRole, number> = {\n owner: 40,\n maintainer: 35,\n collaborator: 25,\n contributor: 15,\n member: 5,\n unknown: 0,\n };\n score += roleBonus[userRole];\n\n // Account age bonus (max +10). #3106: absent → no bonus (avoid NaN; an\n // unknown account neither earns nor loses the age bonus).\n if (metadata.accountAgeDays !== undefined) {\n score += Math.min(metadata.accountAgeDays / DAYS_PER_YEAR_APPROX, 10);\n }\n\n // Contribution bonus (max +10). #3106: absent → no bonus.\n if (metadata.priorContributions !== undefined) {\n score += Math.min(metadata.priorContributions, 10);\n }\n\n // Suspicious signal penalties\n const signalPenalty: Record<SuspiciousSignal, number> = {\n new_account: -15,\n no_prior_contributions: -10,\n injection_patterns_detected: -25,\n rapid_comments: -20,\n mismatched_authority_claim: -30,\n };\n for (const signal of signals) {\n score += signalPenalty[signal];\n }\n\n return Math.max(0, Math.min(100, Math.round(score)));\n}\n\n/** Map role string to GitHubUserRole. */\nfunction mapRole(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 default:\n return 'unknown';\n }\n}\n\n/** Determine effective trust tier from signals and role. */\nfunction determineEffectiveTier(\n userRole: GitHubUserRole,\n signals: readonly SuspiciousSignal[]\n): TrustTier {\n const baseTier = ROLE_DEFAULT_TRUST[userRole];\n\n // Hostile signals → Tier 4\n const hostileSignals: SuspiciousSignal[] = [\n 'injection_patterns_detected',\n 'mismatched_authority_claim',\n ];\n if (signals.some((s) => hostileSignals.includes(s))) return '4';\n\n // Multiple suspicious signals → downgrade by 1\n if (signals.length >= 2) {\n const baseNumeric = TRUST_TIER_NUMERIC[baseTier];\n const downgraded = Math.min(baseNumeric + 1, 4);\n return String(downgraded) as TrustTier;\n }\n\n return baseTier;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Assess a GitHub user's reputation for trust classification.\n *\n * @param metadata - User metadata from GitHub API or local context.\n * @param cache - Optional cache instance for TTL-based deduplication.\n * @returns ReputationAssessment with trust tier and suspicious signals.\n */\nexport function assessReputation(\n metadata: GitHubUserMetadata,\n cache?: ReputationCache\n): ReputationAssessment {\n // Check cache first\n const cached = cache?.get(metadata.username);\n if (cached !== undefined) return cached;\n\n const userRole = mapRole(metadata.authorAssociation);\n const signals = detectSuspiciousSignals(metadata);\n const score = calculateReputationScore(metadata, signals, userRole);\n const effectiveTier = determineEffectiveTier(userRole, signals);\n\n const assessment: ReputationAssessment = {\n username: metadata.username,\n userRole,\n suspiciousSignals: signals,\n isSuspicious: signals.length > 0,\n effectiveTrustTier: effectiveTier,\n reputationScore: score,\n reason: buildReason(userRole, signals, effectiveTier),\n assessedAt: new Date().toISOString(),\n };\n\n cache?.set(metadata.username, assessment);\n return assessment;\n}\n\n/**\n * Reconcile a trust-classifier tier with a reputation assessment into the\n * effective tier to enforce (#3119 / epic #3118). Demotion-only — reputation\n * can only RAISE the tier number (more restrictive), never lower it.\n *\n * Invariants:\n * - **Allowlist/Tier-1 wins**: a classifier Tier 1 (owner/allowlisted maintainer)\n * is authoritative — reputation never demotes it.\n * - **Absent reputation → classifier tier**: no assessment (stage off / not\n * fetched) keeps the classifier/role-default tier — never fabricate a benign\n * tier, never escalate on mere absence (fetch-failure ≠ hostile signal).\n * - **Score is advisory**: only `effectiveTrustTier` participates; the 0–100\n * `reputationScore` never moves the gate.\n */\nexport function reconcileTrustTier(\n classifierTier: TrustTier,\n reputation: ReputationAssessment | undefined\n): TrustTier {\n if (classifierTier === '1') return '1';\n const repTier = reputation?.effectiveTrustTier;\n if (repTier === undefined) return classifierTier;\n return TRUST_TIER_NUMERIC[repTier] > TRUST_TIER_NUMERIC[classifierTier]\n ? repTier\n : classifierTier;\n}\n\n// ============================================================================\n// Reputation gating rollout (#3122 / epic #3118 Phase 4)\n// ============================================================================\n\n/**\n * Rollout mode for reputation-based tier gating, mirroring\n * `NEXUS_ACCESS_POLICY_MODE` (#1977): `off` (no reputation effect), `audit`\n * (compute + report the would-be demotion but enforce the classifier tier), or\n * `enforce` (apply the demotion). Default `audit` — surface telemetry without\n * blocking until the false-positive rate is known, then flip to `enforce`.\n */\nexport const ReputationGatingModeSchema = z.enum(['off', 'audit', 'enforce']);\nexport type ReputationGatingMode = z.infer<typeof ReputationGatingModeSchema>;\n\n/** Default when `NEXUS_REPUTATION_GATING` is unset/invalid — audit (telemetry, no block). */\n/**\n * Default gating mode.\n *\n * Flipped `audit` → `enforce` in #4667. `audit` computed the demotion, logged\n * \"would block under enforce\", and let the actions through — so a detected\n * prompt injection changed nothing in the shipped configuration.\n *\n * Measured before flipping, over the real triage path with only the SCM\n * provider mocked: 5/5 hostile inputs blocked, 0 false positives across five\n * real repository issues (as OWNER and as an unaffiliated author), and 0 across\n * ordinary maintainer language (\"please close this\", \"URGENT\", \"you should\n * merge #88 first\"). The one synthetic false-positive class is a body quoting\n * an injection payload verbatim, which is arguably correct fail-closed\n * behaviour for a security report — and is now visible, because a tier-4\n * outcome emits a `RefuseAction` rather than silently approving less.\n *\n * `NEXUS_REPUTATION_GATING=audit` remains available to roll back.\n */\nexport const DEFAULT_REPUTATION_GATING_MODE: ReputationGatingMode = 'enforce';\n\n/**\n * Resolve the gating mode from the environment (invalid → default + warn, never\n * throws — #3130). Delegates to the shared `resolveEnvMode` so this flag and\n * `NEXUS_ACCESS_POLICY_MODE` coerce identically.\n */\nexport function resolveReputationGatingMode(\n env: NodeJS.ProcessEnv = process.env\n): ReputationGatingMode {\n return resolveEnvMode(\n env['NEXUS_REPUTATION_GATING'],\n ReputationGatingModeSchema,\n DEFAULT_REPUTATION_GATING_MODE,\n 'NEXUS_REPUTATION_GATING'\n );\n}\n\n/** Outcome of applying the gating mode to a reputation assessment. */\nexport interface ReputationGateDecision {\n /** Tier to actually enforce at the policy gate. */\n readonly enforcedTier: TrustTier;\n /** Tier reputation reconciliation computed (what `enforce` mode WOULD use). */\n readonly reconciledTier: TrustTier;\n /** True when reputation would demote but the mode (off/audit) did not enforce it. */\n readonly demotionSuppressed: boolean;\n readonly mode: ReputationGatingMode;\n}\n\n/**\n * Apply the rollout mode to a reputation assessment (#3122). `enforce` gates on\n * the reconciled (possibly demoted) tier; `audit`/`off` gate on the classifier\n * tier but report whether a demotion was suppressed (for telemetry). The\n * Tier-1/allowlist-wins and demotion-only invariants live in `reconcileTrustTier`,\n * so the allowlist remains the escape hatch in every mode.\n */\nexport function gateWithReputation(\n classifierTier: TrustTier,\n reputation: ReputationAssessment | undefined,\n mode: ReputationGatingMode\n): ReputationGateDecision {\n const reconciledTier =\n mode === 'off' ? classifierTier : reconcileTrustTier(classifierTier, reputation);\n const enforcedTier = mode === 'enforce' ? reconciledTier : classifierTier;\n return {\n enforcedTier,\n reconciledTier,\n demotionSuppressed: mode !== 'enforce' && reconciledTier !== classifierTier,\n mode,\n };\n}\n\n/** Build a human-readable reason string. */\nfunction buildReason(\n role: GitHubUserRole,\n signals: readonly SuspiciousSignal[],\n tier: TrustTier\n): string {\n if (signals.length === 0) {\n return `Role ${role} → Tier ${tier} (no suspicious signals)`;\n }\n const signalList = signals.join(', ');\n return `Role ${role} → Tier ${tier} (signals: ${signalList})`;\n}\n","/**\n * Shared resolver for `off`/`audit`/`enforce`-style security-mode env vars\n * (#3130). Both `resolveAccessPolicyMode` (ClawGuard, #1977) and\n * `resolveReputationGatingMode` (reputation gating, #3122) parse an enum env\n * var, coerce an invalid value to a safe default, and must NEVER throw — a\n * security layer must not fail-closed on a misconfiguration at startup.\n *\n * Previously the coercion was silent: a typo'd `enforce` (`enfroce`) degraded\n * to the default with no signal. This helper keeps the never-throw coercion but\n * emits a one-line `warn` so the misconfiguration is observable, and guarantees\n * both flags behave identically.\n *\n * @module security/env-mode\n */\n\nimport type { ZodType } from 'zod';\nimport { createLogger } from '../core/index.js';\nimport type { ILogger } from '../core/index.js';\n\nconst defaultLogger = createLogger({ component: 'env-mode' });\n\n/**\n * Resolve an enum-valued env var to one of its allowed values, coercing an\n * invalid/typo'd value to `fallback`. Unset or empty → `fallback` silently\n * (absence is normal, not a misconfiguration). A non-empty value that fails to\n * parse → `fallback` plus a `warn` (an explicit-but-invalid value is an operator\n * error worth surfacing). Never throws.\n *\n * @param raw The raw env value (e.g. `process.env['NEXUS_X']`).\n * @param schema Zod enum schema for the allowed values.\n * @param fallback Default returned when `raw` is absent/empty/invalid.\n * @param varName Env var name, for the warning message.\n * @param logger Injectable for testing (defaults to the module logger).\n */\nexport function resolveEnvMode<T extends string>(\n raw: string | undefined,\n schema: ZodType<T>,\n fallback: T,\n varName: string,\n logger: ILogger = defaultLogger\n): T {\n if (typeof raw !== 'string' || raw.length === 0) return fallback;\n const parsed = schema.safeParse(raw.toLowerCase());\n if (parsed.success) return parsed.data;\n logger.warn(`Invalid ${varName} value — coercing to default`, { raw, default: fallback });\n return fallback;\n}\n"],"mappings":"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","names":["z","z"]}
1
+ {"version":3,"sources":["../src/security/trust-types.ts","../src/security/input-sanitizer.ts","../src/security/reputation-model.ts","../src/security/env-mode.ts"],"sourcesContent":["/**\n * nexus-agents/security - Trust Types\n *\n * Zod schemas and TypeScript types for the untrusted input hardening\n * framework. Defines trust tiers, sanitized input, user roles, and\n * injection detection flags.\n *\n * @module security/trust-types\n * (Source: Issue #818, #819 — Phase 1: Input Sanitization)\n */\n\nimport { z } from 'zod';\n\n// ============================================================================\n// Trust Tiers\n// ============================================================================\n\n/**\n * Trust tier classification for input sources.\n * Lower number = higher trust.\n *\n * 1 = Authoritative (repo files, CI, CLAUDE.md, allowlisted maintainers)\n * 2 = Semi-trusted (collaborator issue body, contributor PR metadata)\n * 3 = Untrusted (unknown user comments, non-collaborator issue body)\n * 4 = Hostile (injection patterns, hidden HTML, instruction-like content)\n */\nexport const TrustTierSchema = z.enum(['1', '2', '3', '4']);\nexport type TrustTier = z.infer<typeof TrustTierSchema>;\n\n/** Numeric trust tier for comparisons. Higher number = lower trust. */\nexport const TRUST_TIER_NUMERIC: Record<TrustTier, number> = {\n '1': 1,\n '2': 2,\n '3': 3,\n '4': 4,\n};\n\n// ============================================================================\n// GitHub User Roles\n// ============================================================================\n\n/**\n * GitHub user relationship to the repository.\n */\nexport const GitHubUserRoleSchema = z.enum([\n 'owner',\n 'maintainer',\n 'collaborator',\n 'contributor',\n 'member',\n 'unknown',\n]);\nexport type GitHubUserRole = z.infer<typeof GitHubUserRoleSchema>;\n\n/**\n * Default trust tier mapping for each GitHub role.\n * Can be overridden by injection pattern detection (downgrade only).\n */\nexport const ROLE_DEFAULT_TRUST: Record<GitHubUserRole, TrustTier> = {\n owner: '1',\n maintainer: '1',\n collaborator: '2',\n contributor: '2',\n member: '3',\n unknown: '3',\n};\n\n// ============================================================================\n// Injection Detection\n// ============================================================================\n\n/**\n * Categories of injection patterns detected in content.\n */\nexport const InjectionFlagSchema = z.enum([\n 'authority_claim',\n 'instruction_pattern',\n 'system_prompt_manipulation',\n 'hidden_content',\n 'urgency_manipulation',\n 'fake_conversation',\n 'base64_encoded',\n 'external_link_instruction',\n]);\nexport type InjectionFlag = z.infer<typeof InjectionFlagSchema>;\n\n/**\n * An element stripped during sanitization, preserved for audit trail.\n */\nexport const StrippedElementSchema = z.object({\n /** Type of element stripped. */\n tag: z.string().min(1),\n /** Reason for stripping. */\n reason: z.string().min(1),\n /** Start index in original content. */\n startIndex: z.number().int().nonnegative(),\n /** Length of stripped content. */\n length: z.number().int().positive(),\n});\nexport type StrippedElement = z.infer<typeof StrippedElementSchema>;\n\n// ============================================================================\n// Sanitized Input\n// ============================================================================\n\n/**\n * The result of sanitizing untrusted input.\n * Contains cleaned content, trust classification, and audit data.\n */\nexport const SanitizedInputSchema = z.object({\n /** Sanitized content with dangerous elements removed. */\n content: z.string(),\n /** Original content before sanitization (for audit). */\n originalLength: z.number().int().nonnegative(),\n /** Assigned trust tier based on user role and content analysis. */\n trustTier: TrustTierSchema,\n /** GitHub user role of the input source. */\n userRole: GitHubUserRoleSchema,\n /** Injection patterns detected in content. */\n injectionFlags: z.array(InjectionFlagSchema),\n /** Elements stripped during sanitization (audit trail). */\n strippedElements: z.array(StrippedElementSchema),\n /** Whether any dangerous content was detected and stripped. */\n wasModified: z.boolean(),\n /** Timestamp of sanitization (ISO 8601). */\n sanitizedAt: z.iso.datetime(),\n});\nexport type SanitizedInput = z.infer<typeof SanitizedInputSchema>;\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/**\n * Configuration for the input sanitizer.\n */\nexport const SanitizerConfigSchema = z.object({\n /** GitHub usernames that are always Tier 1 (allowlisted maintainers). */\n allowlistedMaintainers: z.array(z.string().min(1)).default([]),\n /** Whether to fail open (log only) or fail closed (block). Phase 1 = open. */\n failOpen: z.boolean().default(true),\n /** Maximum input length before truncation. */\n maxInputLength: z.number().int().positive().default(50_000),\n});\nexport type SanitizerConfig = z.infer<typeof SanitizerConfigSchema>;\n","/**\n * nexus-agents/security - Input Sanitizer\n *\n * Sanitizes untrusted GitHub input by stripping dangerous HTML/XML tags,\n * detecting injection patterns, and producing a SanitizedInput result\n * with full audit trail.\n *\n * Defense layer 1 of the three-layer hardening architecture.\n * See: docs/architecture/UNTRUSTED_INPUT_HARDENING.md\n *\n * @module security/input-sanitizer\n * (Source: Issue #818, #819 — Phase 1: Input Sanitization)\n */\n\nimport type {\n InjectionFlag,\n SanitizedInput,\n SanitizerConfig,\n StrippedElement,\n TrustTier,\n GitHubUserRole,\n} from './trust-types.js';\nimport { ROLE_DEFAULT_TRUST, SanitizerConfigSchema } from './trust-types.js';\n\n// ============================================================================\n// Dangerous HTML Patterns (Trail of Bits / GitHub Copilot vectors)\n// ============================================================================\n\n/**\n * HTML tags to strip. These are known injection vectors:\n * - `<picture>` / `<source>`: Trail of Bits GitHub Copilot injection\n * - `<img>`: Can carry injection via alt text or onerror\n */\nconst DANGEROUS_HTML_PATTERN =\n /<(picture|source|img)\\b[^>]*>[\\s\\S]*?<\\/\\1>|<(picture|source|img)\\b[^>]*\\/?>/gi;\n\n// ============================================================================\n// XML-like Tags (Conversation History Injection)\n// ============================================================================\n\n/**\n * XML-like tags that mimic conversation structure or system prompts.\n */\nconst XML_INJECTION_PATTERN =\n /<\\/?(system|human|assistant|instructions|user|prompt|context|tool_use|tool_result)\\b[^>]*>/gi;\n\n// ============================================================================\n// HTML Comments with Instructions\n// ============================================================================\n\nconst HTML_COMMENT_PATTERN = /<!--[\\s\\S]*?-->/g;\n\n// ============================================================================\n// Injection Pattern Detectors\n// ============================================================================\n\ninterface PatternMatch {\n flag: InjectionFlag;\n pattern: RegExp;\n}\n\nconst INJECTION_PATTERNS: readonly PatternMatch[] = [\n {\n flag: 'authority_claim',\n pattern: /\\b(as (?:a|the) (?:maintainer|admin|owner|security lead|repo owner|developer))\\b/i,\n },\n {\n flag: 'authority_claim',\n pattern: /\\b(i(?:'m| am) the (?:repo |project )?(?:owner|maintainer|admin))\\b/i,\n },\n {\n flag: 'instruction_pattern',\n pattern: /\\b(please (?:close|merge|label|mark|apply|delete|remove|approve|reject))\\b/i,\n },\n {\n flag: 'instruction_pattern',\n pattern: /\\b(you (?:should|must|need to) (?:close|merge|label|apply|delete))\\b/i,\n },\n {\n flag: 'system_prompt_manipulation',\n pattern: /\\b(ignore (?:all )?previous (?:instructions|rules|prompts))\\b/i,\n },\n {\n flag: 'system_prompt_manipulation',\n pattern: /\\b(forget (?:your |all )?(?:instructions|rules|safety))\\b/i,\n },\n {\n flag: 'system_prompt_manipulation',\n pattern: /\\b(new (?:instructions|rules|system prompt|directives))\\b/i,\n },\n {\n flag: 'urgency_manipulation',\n pattern: /\\b(critical|emergency|urgent|must act now|immediately|time[- ]?sensitive)\\b/i,\n },\n {\n flag: 'fake_conversation',\n pattern: /<(?:assistant|human|user|system)>/i,\n },\n // base64_encoded is detected separately by `looksLikeBase64Payload` below.\n // The lookahead-based regex it replaced exhibited catastrophic backtracking\n // on long hex-only inputs (#2191) — see `looksLikeBase64Payload` for the\n // two-phase rewrite that preserves the #1811 SHA-hash false-positive guard.\n {\n flag: 'external_link_instruction',\n pattern: /(?:apply|run|execute|install)\\s+(?:this\\s+)?(?:from\\s+)?https?:\\/\\//i,\n },\n];\n\n/**\n * Two-phase base64-payload detection (#2191).\n *\n * The original `(?=[A-Za-z0-9+/]*[g-zG-Z+/=])[A-Za-z0-9+/]{40,}={0,2}` regex\n * was vulnerable to catastrophic backtracking — V8 took ~1.8s on a 50K\n * hex-only adversarial input. Splitting the check into two non-overlapping\n * phases removes the lookahead and makes the worst case linear.\n *\n * Phase 1: find a 40+ run of base64-alphabet chars (no lookahead).\n * Phase 2: confirm the matched substring contains a base64-discriminating\n * char (g-z, G-Z, +, /, =) so SHA-1 / SHA-256 hex hashes don't\n * false-positive (preserves #1811 behavior).\n *\n * Same detection coverage as the original; same false-positive resistance.\n */\nconst BASE64_RUN_GLOBAL = /[A-Za-z0-9+/]{40,}={0,2}/g;\nconst BASE64_DISCRIMINATOR = /[g-zG-Z+/=]/;\n\nfunction looksLikeBase64Payload(content: string): boolean {\n // Iterate every 40+ base64-alphabet run rather than just the first one,\n // so a long hex-only prefix doesn't mask a real base64 payload that\n // appears later in the content.\n for (const match of content.matchAll(BASE64_RUN_GLOBAL)) {\n if (BASE64_DISCRIMINATOR.test(match[0])) return true;\n }\n return false;\n}\n\n// ============================================================================\n// HTML Entity Decoding (evasion defense)\n// ============================================================================\n\n/**\n * Dangerous-tag names we will still match after entity decoding.\n * Kept in sync with DANGEROUS_HTML_PATTERN and XML_INJECTION_PATTERN above.\n */\nconst DANGEROUS_TAG_NAMES =\n 'picture|source|img|system|human|assistant|instructions|user|prompt|context|tool_use|tool_result';\n\n/**\n * Detects whether the input contains entity-encoded forms of any dangerous\n * tag (&lt;picture, &#60;system, &#x3c;img …). Used as a cheap pre-check so\n * that benign content with legitimate entities (e.g. \"AT&amp;T\") is passed\n * through untouched and wasModified stays false.\n */\nconst ENCODED_DANGEROUS_TAG_PATTERN = new RegExp(\n `&(?:lt|#0*60|#x0*3c);\\\\s*\\\\/?\\\\s*(?:${DANGEROUS_TAG_NAMES})\\\\b`,\n 'i'\n);\n\n/** Decodes the subset of HTML entities that can reconstruct tag syntax. */\nfunction decodeEntities(content: string): string {\n return (\n content\n // Numeric (decimal) references\n .replace(/&#(\\d+);/g, (_match, dec: string) => {\n const code = Number.parseInt(dec, 10);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _match;\n })\n // Numeric (hex) references\n .replace(/&#x([0-9a-f]+);/gi, (_match, hex: string) => {\n const code = Number.parseInt(hex, 16);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _match;\n })\n // Named entities most relevant to tag reconstruction\n .replace(/&lt;/gi, '<')\n .replace(/&gt;/gi, '>')\n .replace(/&quot;/gi, '\"')\n .replace(/&apos;/gi, \"'\")\n // &amp; must be decoded last so that &amp;lt; does not resurface as <\n .replace(/&amp;/gi, '&')\n );\n}\n\n/**\n * Runs decodeEntities only if the input contains an entity-encoded dangerous\n * tag. This keeps benign content with legitimate entities untouched.\n */\nfunction applyEntityEvasionDefense(content: string): {\n cleaned: string;\n stripped: StrippedElement[];\n} {\n if (!ENCODED_DANGEROUS_TAG_PATTERN.test(content)) {\n return { cleaned: content, stripped: [] };\n }\n const decoded = decodeEntities(content);\n return {\n cleaned: decoded,\n stripped: [\n {\n tag: '&…;',\n reason: 'HTML entity-encoded dangerous tag decoded for stripping (CWE-79)',\n startIndex: 0,\n length: content.length,\n },\n ],\n };\n}\n\n// ============================================================================\n// Core Sanitization Functions\n// ============================================================================\n\n/** Strips dangerous HTML tags and records what was removed.\n * Loops until stable to prevent reconstructed patterns after removal (#1496). */\nfunction stripDangerousHtml(content: string): {\n cleaned: string;\n stripped: StrippedElement[];\n} {\n const stripped: StrippedElement[] = [];\n let cleaned = content;\n const MAX_PASSES = 5;\n for (let pass = 0; pass < MAX_PASSES; pass++) {\n DANGEROUS_HTML_PATTERN.lastIndex = 0;\n if (!DANGEROUS_HTML_PATTERN.test(cleaned)) break;\n cleaned = cleaned.replace(DANGEROUS_HTML_PATTERN, (match, _g1, _g2, offset: number) => {\n stripped.push({\n tag: match.slice(0, 30) + (match.length > 30 ? '...' : ''),\n reason: 'Dangerous HTML tag (Trail of Bits injection vector)',\n startIndex: offset,\n length: match.length,\n });\n return '';\n });\n }\n return { cleaned, stripped };\n}\n\n/** Strips XML-like tags that mimic conversation structure.\n * Loops until stable to prevent reconstructed patterns after removal (#1496). */\nfunction stripXmlTags(content: string): {\n cleaned: string;\n stripped: StrippedElement[];\n} {\n const stripped: StrippedElement[] = [];\n let cleaned = content;\n const MAX_PASSES = 5;\n for (let pass = 0; pass < MAX_PASSES; pass++) {\n XML_INJECTION_PATTERN.lastIndex = 0;\n if (!XML_INJECTION_PATTERN.test(cleaned)) break;\n cleaned = cleaned.replace(XML_INJECTION_PATTERN, (match, _g1, offset: number) => {\n stripped.push({\n tag: match,\n reason: 'XML-like conversation injection tag',\n startIndex: offset,\n length: match.length,\n });\n return '';\n });\n }\n return { cleaned, stripped };\n}\n\n/** Strips HTML comments that may contain hidden instructions.\n * Loops until stable to prevent reconstructed comment patterns (#1496). */\nfunction stripHtmlComments(content: string): {\n cleaned: string;\n stripped: StrippedElement[];\n} {\n const stripped: StrippedElement[] = [];\n let cleaned = content;\n // Loop until stable: stripping may reveal new instruction-bearing comments\n const MAX_PASSES = 5;\n for (let pass = 0; pass < MAX_PASSES; pass++) {\n HTML_COMMENT_PATTERN.lastIndex = 0;\n const prevLength = cleaned.length;\n cleaned = cleaned.replace(HTML_COMMENT_PATTERN, (match, offset: number) => {\n const hasInstruction = /\\b(ignore|execute|close|merge|delete|apply)\\b/i.test(match);\n if (!hasInstruction) return match;\n\n stripped.push({\n tag: '<!-- ... -->',\n reason: 'HTML comment with instruction-like content',\n startIndex: offset,\n length: match.length,\n });\n return '';\n });\n if (cleaned.length === prevLength) break;\n }\n // Second pass: strip unclosed <!-- tags (incomplete comment injection)\n let searchFrom = 0;\n while (searchFrom < cleaned.length) {\n const openIdx = cleaned.indexOf('<!--', searchFrom);\n if (openIdx === -1) break;\n const closeIdx = cleaned.indexOf('-->', openIdx + 4);\n if (closeIdx === -1) {\n stripped.push({\n tag: '<!--',\n reason: 'Unclosed HTML comment (potential injection vector)',\n startIndex: openIdx,\n length: cleaned.length - openIdx,\n });\n cleaned = cleaned.slice(0, openIdx);\n break;\n }\n searchFrom = closeIdx + 3;\n }\n return { cleaned, stripped };\n}\n\n/** Detects injection patterns in content without modifying it. */\nfunction detectInjectionPatterns(content: string): InjectionFlag[] {\n const flags = new Set<InjectionFlag>();\n for (const { flag, pattern } of INJECTION_PATTERNS) {\n // Reset lastIndex for global patterns\n pattern.lastIndex = 0;\n if (pattern.test(content)) {\n flags.add(flag);\n }\n }\n if (looksLikeBase64Payload(content)) {\n flags.add('base64_encoded');\n }\n return Array.from(flags);\n}\n\n/** Lower-cases an arbitrary role literal and maps it to a known role. */\nfunction normalizeRole(userRole: string): GitHubUserRole {\n const lower = userRole.toLowerCase();\n if (lower in ROLE_DEFAULT_TRUST) return lower as GitHubUserRole;\n return 'unknown';\n}\n\n/**\n * Assigns trust tier based on user role and injection analysis.\n * Injection patterns can only DOWNGRADE trust, never upgrade.\n */\nfunction assignTrustTier(\n userRole: GitHubUserRole,\n injectionFlags: readonly InjectionFlag[],\n allowlisted: boolean\n): TrustTier {\n if (allowlisted) return '1';\n\n // Defensive lowercase — the type says GitHubUserRole, but callers\n // sometimes cast an unnormalized GitHub author_association literal\n // (e.g. 'OWNER', 'MEMBER') directly. Normalize here so the maintainer\n // exemption below matches regardless of case (CWE-178).\n const normalizedRole = normalizeRole(userRole);\n const baseTier = ROLE_DEFAULT_TRUST[normalizedRole];\n\n // Content with injection patterns is downgraded to Tier 4 (hostile)\n const hostileFlags: InjectionFlag[] = ['system_prompt_manipulation', 'fake_conversation'];\n if (injectionFlags.some((f) => hostileFlags.includes(f))) return '4';\n\n // Content with authority claims from non-maintainers is suspicious\n if (\n injectionFlags.includes('authority_claim') &&\n normalizedRole !== 'owner' &&\n normalizedRole !== 'maintainer'\n ) {\n return '4';\n }\n\n return baseTier;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Sanitizes untrusted GitHub input through the full Layer 1 pipeline:\n * 1. HTML stripping (picture/source/img tags)\n * 2. XML tag stripping (system/human/assistant)\n * 3. HTML comment stripping (instruction-bearing comments only)\n * 4. Injection pattern detection\n * 5. Trust tier assignment\n *\n * ⚠ **Use HostileInputFirewall.process() in agent code paths.** Calling\n * sanitizeInput() directly only runs Layer 1 — it does not evaluate the\n * Rule of Two (enforced in policy-gate.ts via evaluatePolicy) and does\n * not emit audit-trail events. An agent that processes untrusted input\n * while holding both write access and secrets violates the Rule of Two;\n * the policy gate is what catches this, and it only runs inside the\n * firewall pipeline. Direct use of this function is appropriate for\n * unit tests and pure content analysis, not for agent decision paths.\n *\n * @see packages/nexus-agents/src/security/firewall/firewall-pipeline.ts\n * @see packages/nexus-agents/src/security/policy-gate.ts\n * @param content - Raw untrusted content from GitHub\n * @param userRole - GitHub user's relationship to the repository\n * @param username - GitHub username (for allowlist check)\n * @param config - Optional sanitizer configuration\n * @returns SanitizedInput with cleaned content and audit data\n */\nexport function sanitizeInput(\n content: string,\n userRole: GitHubUserRole,\n username: string,\n config?: Partial<SanitizerConfig>\n): SanitizedInput {\n const cfg = SanitizerConfigSchema.parse(config ?? {});\n const truncated = content.slice(0, cfg.maxInputLength);\n const allowlisted = cfg.allowlistedMaintainers.includes(username);\n\n try {\n // Pipeline: decode entity-encoded dangerous tags, then strip dangerous content\n const entityDecoded = applyEntityEvasionDefense(truncated);\n const html = stripDangerousHtml(entityDecoded.cleaned);\n const xml = stripXmlTags(html.cleaned);\n const comments = stripHtmlComments(xml.cleaned);\n const allStripped = [\n ...entityDecoded.stripped,\n ...html.stripped,\n ...xml.stripped,\n ...comments.stripped,\n ];\n\n // Detect injection patterns on ORIGINAL content (before stripping)\n const injectionFlags = detectInjectionPatterns(truncated);\n\n // Assign trust tier\n const trustTier = assignTrustTier(userRole, injectionFlags, allowlisted);\n\n return {\n content: comments.cleaned,\n originalLength: content.length,\n trustTier,\n userRole,\n injectionFlags,\n strippedElements: allStripped,\n wasModified: allStripped.length > 0,\n sanitizedAt: new Date().toISOString(),\n };\n } catch (err: unknown) {\n return buildFailClosedResult(err, content, truncated, userRole, allowlisted);\n }\n}\n\n/**\n * Fail-closed result returned when the sanitizer pipeline throws.\n * Returns empty content at Tier-4 (or Tier-1 for allowlisted maintainers,\n * since their bypass is not regex-dependent) so downstream consumers\n * cannot act on untrusted content we could not validate.\n * CLAUDE.md: \"Fail closed on ambiguity.\"\n */\nfunction buildFailClosedResult(\n err: unknown,\n originalContent: string,\n truncated: string,\n userRole: GitHubUserRole,\n allowlisted: boolean\n): SanitizedInput {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: '',\n originalLength: originalContent.length,\n trustTier: allowlisted ? '1' : '4',\n userRole,\n injectionFlags: [],\n strippedElements: [\n {\n tag: '(pipeline-failure)',\n reason: `Sanitizer pipeline threw; input discarded as fail-closed: ${message}`,\n startIndex: 0,\n length: truncated.length,\n },\n ],\n wasModified: true,\n sanitizedAt: new Date().toISOString(),\n };\n}\n","/**\n * nexus-agents/security - Reputation Model\n *\n * Lightweight trust model for GitHub users that assesses reputation\n * based on account age, contribution history, and behavioral signals.\n * Integrates with the trust classifier for comprehensive trust assessment.\n *\n * @module security/reputation-model\n * (Source: Issue #818, #824 — Phase 3: Reputation Model)\n */\n\nimport { z } from 'zod';\n\nimport { getTimeProvider } from '../core/index.js';\nimport { resolveEnvMode } from './env-mode.js';\nimport type { TrustTier, GitHubUserRole, InjectionFlag } from './trust-types.js';\nimport { TRUST_TIER_NUMERIC, ROLE_DEFAULT_TRUST } from './trust-types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Signals that indicate a suspicious actor.\n */\nexport const SuspiciousSignalSchema = z.enum([\n 'new_account',\n 'no_prior_contributions',\n 'injection_patterns_detected',\n 'rapid_comments',\n 'mismatched_authority_claim',\n]);\nexport type SuspiciousSignal = z.infer<typeof SuspiciousSignalSchema>;\n\n/**\n * GitHub user metadata for reputation assessment.\n */\nexport interface GitHubUserMetadata {\n readonly username: string;\n /**\n * Account/activity fields are OPTIONAL (#3106). When a field is absent (the\n * caller couldn't fetch it — e.g. the firewall before Phase 3 wiring), its\n * signal is SKIPPED rather than fabricated: an unknown value must never be\n * treated as benign (the old hardcoded `365`/`0`) nor as hostile. Only the\n * `authorAssociation` + `injectionFlags` signals fire on absent activity data.\n */\n readonly accountAgeDays?: number;\n readonly priorContributions?: number;\n readonly recentCommentCount?: number;\n readonly recentCommentWindowMinutes?: number;\n readonly authorAssociation: string;\n readonly injectionFlags: readonly InjectionFlag[];\n}\n\n/**\n * Result of a reputation assessment.\n */\nexport interface ReputationAssessment {\n readonly username: string;\n readonly userRole: GitHubUserRole;\n readonly suspiciousSignals: readonly SuspiciousSignal[];\n readonly isSuspicious: boolean;\n readonly effectiveTrustTier: TrustTier;\n readonly reputationScore: number;\n readonly reason: string;\n readonly assessedAt: string;\n}\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/** Thresholds for suspicious behavior detection. */\n/** Approximate days per year, used to normalize account age to a 0–10 scale. */\nconst DAYS_PER_YEAR_APPROX = 36.5;\n\nconst SUSPICIOUS_THRESHOLDS = {\n /** Account younger than this (days) is flagged. */\n newAccountDays: 30,\n /** Fewer contributions than this is flagged. */\n minContributions: 1,\n /** More comments than this in the window triggers rapid-comment flag. */\n rapidCommentThreshold: 5,\n /** Time window (minutes) for rapid comment detection. */\n rapidCommentWindowMinutes: 10,\n} as const;\n\n// ============================================================================\n// Reputation Cache\n// ============================================================================\n\ninterface CacheEntry {\n assessment: ReputationAssessment;\n expiresAt: number;\n}\n\n// Canonical source: config/timeouts.ts (Issue #1046)\nimport { CACHE_TIMEOUTS } from '../config/timeouts.js';\n\nconst DEFAULT_TTL_MS: number = CACHE_TIMEOUTS.reputationTtlMs;\nconst DEFAULT_MAX_SIZE = 1000;\n\n/**\n * In-memory reputation cache with TTL and max size.\n * Reduces redundant assessments for the same user within a short window.\n * Evicts oldest entries when max size is exceeded.\n */\nexport class ReputationCache {\n private readonly cache = new Map<string, CacheEntry>();\n private readonly ttlMs: number;\n private readonly maxSize: number;\n\n constructor(ttlMs = DEFAULT_TTL_MS, maxSize = DEFAULT_MAX_SIZE) {\n this.ttlMs = ttlMs;\n this.maxSize = maxSize;\n }\n\n get(username: string): ReputationAssessment | undefined {\n const entry = this.cache.get(username);\n if (entry === undefined) return undefined;\n if (getTimeProvider().now() > entry.expiresAt) {\n this.cache.delete(username);\n return undefined;\n }\n return entry.assessment;\n }\n\n set(username: string, assessment: ReputationAssessment): void {\n if (this.cache.size >= this.maxSize && !this.cache.has(username)) {\n this.evictOldest();\n }\n this.cache.set(username, {\n assessment,\n expiresAt: getTimeProvider().now() + this.ttlMs,\n });\n }\n\n /** Evict a batch of oldest entries (10% of maxSize, minimum 1). */\n private evictOldest(): void {\n const batchSize = Math.max(1, Math.floor(this.maxSize * 0.1));\n const keys = this.cache.keys();\n for (let i = 0; i < batchSize; i++) {\n const next = keys.next();\n if (next.done === true) break;\n this.cache.delete(next.value);\n }\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n\n// ============================================================================\n// Suspicious Signal Detection\n// ============================================================================\n\n/** Only hostile-tier injection flags count — benign flags like\n * instruction_pattern (\"please remove\") must not trip the injection signal. */\nconst HOSTILE_INJECTION_FLAGS: readonly InjectionFlag[] = [\n 'system_prompt_manipulation',\n 'fake_conversation',\n 'authority_claim',\n 'hidden_content',\n];\n\nfunction hasHostileInjection(flags: readonly InjectionFlag[]): boolean {\n return flags.some((f) => HOSTILE_INJECTION_FLAGS.includes(f));\n}\n\n/** Rapid-comment burst — only when both count and window are known (#3106). */\nfunction isRapidCommenting(m: GitHubUserMetadata): boolean {\n return (\n m.recentCommentCount !== undefined &&\n m.recentCommentWindowMinutes !== undefined &&\n m.recentCommentCount > SUSPICIOUS_THRESHOLDS.rapidCommentThreshold &&\n m.recentCommentWindowMinutes <= SUSPICIOUS_THRESHOLDS.rapidCommentWindowMinutes\n );\n}\n\n/** Authority claim from a non-maintainer role. */\nfunction isMismatchedAuthority(m: GitHubUserMetadata): boolean {\n if (!m.injectionFlags.includes('authority_claim')) return false;\n const association = m.authorAssociation.toUpperCase();\n return association !== 'OWNER' && association !== 'MEMBER';\n}\n\n/** Detect all suspicious signals from user metadata. #3106: account/activity\n * signals are skipped when their data is absent — never fabricated. */\nfunction detectSuspiciousSignals(metadata: GitHubUserMetadata): SuspiciousSignal[] {\n const signals: SuspiciousSignal[] = [];\n const { accountAgeDays, priorContributions } = metadata;\n\n if (accountAgeDays !== undefined && accountAgeDays < SUSPICIOUS_THRESHOLDS.newAccountDays) {\n signals.push('new_account');\n }\n if (\n priorContributions !== undefined &&\n priorContributions < SUSPICIOUS_THRESHOLDS.minContributions\n ) {\n signals.push('no_prior_contributions');\n }\n if (hasHostileInjection(metadata.injectionFlags)) signals.push('injection_patterns_detected');\n if (isRapidCommenting(metadata)) signals.push('rapid_comments');\n if (isMismatchedAuthority(metadata)) signals.push('mismatched_authority_claim');\n\n return signals;\n}\n\n/** Calculate a 0-100 reputation score. */\nfunction calculateReputationScore(\n metadata: GitHubUserMetadata,\n signals: readonly SuspiciousSignal[],\n userRole: GitHubUserRole\n): number {\n let score = 50; // baseline\n\n // Role bonus\n const roleBonus: Record<GitHubUserRole, number> = {\n owner: 40,\n maintainer: 35,\n collaborator: 25,\n contributor: 15,\n member: 5,\n unknown: 0,\n };\n score += roleBonus[userRole];\n\n // Account age bonus (max +10). #3106: absent → no bonus (avoid NaN; an\n // unknown account neither earns nor loses the age bonus).\n if (metadata.accountAgeDays !== undefined) {\n score += Math.min(metadata.accountAgeDays / DAYS_PER_YEAR_APPROX, 10);\n }\n\n // Contribution bonus (max +10). #3106: absent → no bonus.\n if (metadata.priorContributions !== undefined) {\n score += Math.min(metadata.priorContributions, 10);\n }\n\n // Suspicious signal penalties\n const signalPenalty: Record<SuspiciousSignal, number> = {\n new_account: -15,\n no_prior_contributions: -10,\n injection_patterns_detected: -25,\n rapid_comments: -20,\n mismatched_authority_claim: -30,\n };\n for (const signal of signals) {\n score += signalPenalty[signal];\n }\n\n return Math.max(0, Math.min(100, Math.round(score)));\n}\n\n/** Map role string to GitHubUserRole. */\nfunction mapRole(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 default:\n return 'unknown';\n }\n}\n\n/** Determine effective trust tier from signals and role. */\nfunction determineEffectiveTier(\n userRole: GitHubUserRole,\n signals: readonly SuspiciousSignal[]\n): TrustTier {\n const baseTier = ROLE_DEFAULT_TRUST[userRole];\n\n // Hostile signals → Tier 4\n const hostileSignals: SuspiciousSignal[] = [\n 'injection_patterns_detected',\n 'mismatched_authority_claim',\n ];\n if (signals.some((s) => hostileSignals.includes(s))) return '4';\n\n // Multiple suspicious signals → downgrade by 1, CLAMPED AT TIER 3 (#4681).\n //\n // Tier 4 is the HOSTILE tier — `.rules/untrusted-input.md` defines it as\n // \"injection patterns, hidden HTML, instruction-like content\". It is reached\n // ONLY via `hostileSignals` above. The generic downgrade must not manufacture\n // it, because the two most common signals (`new_account` and\n // `no_prior_contributions`) BOTH fire automatically for any genuine\n // first-time reporter — they are two readings of one fact, not independent\n // evidence of hostility. Clamping at 4 meant an `unknown` author (base 3)\n // filing an ordinary bug report was classified hostile; once enforce mode\n // became the default (#4675) that refused every new account's first issue\n // and escalated it to security.\n //\n // Tier 3 is the correct destination: \"untrusted\" — still processed, but\n // never treated as instructions.\n if (signals.length >= 2) {\n const baseNumeric = TRUST_TIER_NUMERIC[baseTier];\n const downgraded = Math.min(baseNumeric + 1, 3);\n return String(downgraded) as TrustTier;\n }\n\n return baseTier;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Assess a GitHub user's reputation for trust classification.\n *\n * @param metadata - User metadata from GitHub API or local context.\n * @param cache - Optional cache instance for TTL-based deduplication.\n * @returns ReputationAssessment with trust tier and suspicious signals.\n */\nexport function assessReputation(\n metadata: GitHubUserMetadata,\n cache?: ReputationCache\n): ReputationAssessment {\n // Check cache first\n const cached = cache?.get(metadata.username);\n if (cached !== undefined) return cached;\n\n const userRole = mapRole(metadata.authorAssociation);\n const signals = detectSuspiciousSignals(metadata);\n const score = calculateReputationScore(metadata, signals, userRole);\n const effectiveTier = determineEffectiveTier(userRole, signals);\n\n const assessment: ReputationAssessment = {\n username: metadata.username,\n userRole,\n suspiciousSignals: signals,\n isSuspicious: signals.length > 0,\n effectiveTrustTier: effectiveTier,\n reputationScore: score,\n reason: buildReason(userRole, signals, effectiveTier),\n assessedAt: new Date().toISOString(),\n };\n\n cache?.set(metadata.username, assessment);\n return assessment;\n}\n\n/**\n * Reconcile a trust-classifier tier with a reputation assessment into the\n * effective tier to enforce (#3119 / epic #3118). Demotion-only — reputation\n * can only RAISE the tier number (more restrictive), never lower it.\n *\n * Invariants:\n * - **Allowlist/Tier-1 wins**: a classifier Tier 1 (owner/allowlisted maintainer)\n * is authoritative — reputation never demotes it.\n * - **Absent reputation → classifier tier**: no assessment (stage off / not\n * fetched) keeps the classifier/role-default tier — never fabricate a benign\n * tier, never escalate on mere absence (fetch-failure ≠ hostile signal).\n * - **Score is advisory**: only `effectiveTrustTier` participates; the 0–100\n * `reputationScore` never moves the gate.\n */\nexport function reconcileTrustTier(\n classifierTier: TrustTier,\n reputation: ReputationAssessment | undefined\n): TrustTier {\n if (classifierTier === '1') return '1';\n const repTier = reputation?.effectiveTrustTier;\n if (repTier === undefined) return classifierTier;\n return TRUST_TIER_NUMERIC[repTier] > TRUST_TIER_NUMERIC[classifierTier]\n ? repTier\n : classifierTier;\n}\n\n// ============================================================================\n// Reputation gating rollout (#3122 / epic #3118 Phase 4)\n// ============================================================================\n\n/**\n * Rollout mode for reputation-based tier gating, mirroring\n * `NEXUS_ACCESS_POLICY_MODE` (#1977): `off` (no reputation effect), `audit`\n * (compute + report the would-be demotion but enforce the classifier tier), or\n * `enforce` (apply the demotion). Default `enforce` since #4667 — see\n * {@link DEFAULT_REPUTATION_GATING_MODE} for the measurement that justified the\n * flip. (This block previously still described the pre-flip `audit` default.)\n */\nexport const ReputationGatingModeSchema = z.enum(['off', 'audit', 'enforce']);\nexport type ReputationGatingMode = z.infer<typeof ReputationGatingModeSchema>;\n\n/**\n * Default gating mode.\n *\n * Flipped `audit` → `enforce` in #4667. `audit` computed the demotion, logged\n * \"would block under enforce\", and let the actions through — so a detected\n * prompt injection changed nothing in the shipped configuration.\n *\n * Measured before flipping, over the real triage path with only the SCM\n * provider mocked: 5/5 hostile inputs blocked, 0 false positives across five\n * real repository issues (as OWNER and as an unaffiliated author), and 0 across\n * ordinary maintainer language (\"please close this\", \"URGENT\", \"you should\n * merge #88 first\"). The one synthetic false-positive class is a body quoting\n * an injection payload verbatim, which is arguably correct fail-closed\n * behaviour for a security report — and is now visible, because a tier-4\n * outcome emits a `RefuseAction` rather than silently approving less.\n *\n * `NEXUS_REPUTATION_GATING=audit` remains available to roll back.\n */\nexport const DEFAULT_REPUTATION_GATING_MODE: ReputationGatingMode = 'enforce';\n\n/**\n * Resolve the gating mode from the environment (invalid → default + warn, never\n * throws — #3130). Delegates to the shared `resolveEnvMode` so this flag and\n * `NEXUS_ACCESS_POLICY_MODE` coerce identically.\n */\nexport function resolveReputationGatingMode(\n env: NodeJS.ProcessEnv = process.env\n): ReputationGatingMode {\n return resolveEnvMode(\n env['NEXUS_REPUTATION_GATING'],\n ReputationGatingModeSchema,\n DEFAULT_REPUTATION_GATING_MODE,\n 'NEXUS_REPUTATION_GATING'\n );\n}\n\n/** Outcome of applying the gating mode to a reputation assessment. */\nexport interface ReputationGateDecision {\n /** Tier to actually enforce at the policy gate. */\n readonly enforcedTier: TrustTier;\n /** Tier reputation reconciliation computed (what `enforce` mode WOULD use). */\n readonly reconciledTier: TrustTier;\n /** True when reputation would demote but the mode (off/audit) did not enforce it. */\n readonly demotionSuppressed: boolean;\n readonly mode: ReputationGatingMode;\n}\n\n/**\n * Apply the rollout mode to a reputation assessment (#3122). `enforce` gates on\n * the reconciled (possibly demoted) tier; `audit`/`off` gate on the classifier\n * tier but report whether a demotion was suppressed (for telemetry). The\n * Tier-1/allowlist-wins and demotion-only invariants live in `reconcileTrustTier`,\n * so the allowlist remains the escape hatch in every mode.\n */\nexport function gateWithReputation(\n classifierTier: TrustTier,\n reputation: ReputationAssessment | undefined,\n mode: ReputationGatingMode\n): ReputationGateDecision {\n const reconciledTier =\n mode === 'off' ? classifierTier : reconcileTrustTier(classifierTier, reputation);\n const enforcedTier = mode === 'enforce' ? reconciledTier : classifierTier;\n return {\n enforcedTier,\n reconciledTier,\n demotionSuppressed: mode !== 'enforce' && reconciledTier !== classifierTier,\n mode,\n };\n}\n\n/** Build a human-readable reason string. */\nfunction buildReason(\n role: GitHubUserRole,\n signals: readonly SuspiciousSignal[],\n tier: TrustTier\n): string {\n if (signals.length === 0) {\n return `Role ${role} → Tier ${tier} (no suspicious signals)`;\n }\n const signalList = signals.join(', ');\n return `Role ${role} → Tier ${tier} (signals: ${signalList})`;\n}\n","/**\n * Shared resolver for `off`/`audit`/`enforce`-style security-mode env vars\n * (#3130). Both `resolveAccessPolicyMode` (ClawGuard, #1977) and\n * `resolveReputationGatingMode` (reputation gating, #3122) parse an enum env\n * var, coerce an invalid value to a safe default, and must NEVER throw — a\n * security layer must not fail-closed on a misconfiguration at startup.\n *\n * Previously the coercion was silent: a typo'd `enforce` (`enfroce`) degraded\n * to the default with no signal. This helper keeps the never-throw coercion but\n * emits a one-line `warn` so the misconfiguration is observable, and guarantees\n * both flags behave identically.\n *\n * @module security/env-mode\n */\n\nimport type { ZodType } from 'zod';\nimport { createLogger } from '../core/index.js';\nimport type { ILogger } from '../core/index.js';\n\nconst defaultLogger = createLogger({ component: 'env-mode' });\n\n/**\n * Resolve an enum-valued env var to one of its allowed values, coercing an\n * invalid/typo'd value to `fallback`. Unset or empty → `fallback` silently\n * (absence is normal, not a misconfiguration). A non-empty value that fails to\n * parse → `fallback` plus a `warn` (an explicit-but-invalid value is an operator\n * error worth surfacing). Never throws.\n *\n * @param raw The raw env value (e.g. `process.env['NEXUS_X']`).\n * @param schema Zod enum schema for the allowed values.\n * @param fallback Default returned when `raw` is absent/empty/invalid.\n * @param varName Env var name, for the warning message.\n * @param logger Injectable for testing (defaults to the module logger).\n */\nexport function resolveEnvMode<T extends string>(\n raw: string | undefined,\n schema: ZodType<T>,\n fallback: T,\n varName: string,\n logger: ILogger = defaultLogger\n): T {\n if (typeof raw !== 'string' || raw.length === 0) return fallback;\n const parsed = schema.safeParse(raw.toLowerCase());\n if (parsed.success) return parsed.data;\n logger.warn(`Invalid ${varName} value — coercing to default`, { raw, default: fallback });\n return fallback;\n}\n"],"mappings":"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","names":["z","z"]}
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  GitHubProvider,
3
3
  ScmError
4
- } from "./chunk-3WWE26PG.js";
4
+ } from "./chunk-GSRW2H3C.js";
5
5
  import {
6
6
  resolveToken
7
- } from "./chunk-G6AVXG6F.js";
7
+ } from "./chunk-LI5RF3H7.js";
8
8
  import {
9
9
  err,
10
10
  ok
11
- } from "./chunk-6VUIQQ3O.js";
11
+ } from "./chunk-KTYTESEV.js";
12
12
 
13
13
  // src/scm/factory.ts
14
14
  async function createScmProvider(config) {
@@ -41,4 +41,4 @@ export {
41
41
  createScmProvider,
42
42
  createGitHubProvider
43
43
  };
44
- //# sourceMappingURL=chunk-FJKMNLTU.js.map
44
+ //# sourceMappingURL=chunk-D34SUVBO.js.map