open-multi-agent-kit 0.90.7 → 0.90.9

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 (331) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +17 -10
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +5 -1
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/commands/doctor-provider-cli.d.ts +33 -0
  7. package/dist/commands/doctor-provider-cli.d.ts.map +1 -0
  8. package/dist/commands/doctor-provider-cli.js +215 -0
  9. package/dist/commands/doctor-provider-cli.js.map +1 -0
  10. package/dist/commands/doctor-provider-transport.d.ts +35 -0
  11. package/dist/commands/doctor-provider-transport.d.ts.map +1 -0
  12. package/dist/commands/doctor-provider-transport.js +185 -0
  13. package/dist/commands/doctor-provider-transport.js.map +1 -0
  14. package/dist/commands/doctor-provider.d.ts +121 -7
  15. package/dist/commands/doctor-provider.d.ts.map +1 -1
  16. package/dist/commands/doctor-provider.js +1295 -102
  17. package/dist/commands/doctor-provider.js.map +1 -1
  18. package/dist/commands/session-doctor-cli.d.ts +43 -0
  19. package/dist/commands/session-doctor-cli.d.ts.map +1 -0
  20. package/dist/commands/session-doctor-cli.js +494 -0
  21. package/dist/commands/session-doctor-cli.js.map +1 -0
  22. package/dist/commands/verify-ci.d.ts +21 -0
  23. package/dist/commands/verify-ci.d.ts.map +1 -0
  24. package/dist/commands/verify-ci.js +66 -0
  25. package/dist/commands/verify-ci.js.map +1 -0
  26. package/dist/core/agent-session-services.d.ts +3 -0
  27. package/dist/core/agent-session-services.d.ts.map +1 -1
  28. package/dist/core/agent-session-services.js +3 -0
  29. package/dist/core/agent-session-services.js.map +1 -1
  30. package/dist/core/agent-session.d.ts +116 -2
  31. package/dist/core/agent-session.d.ts.map +1 -1
  32. package/dist/core/agent-session.js +1015 -71
  33. package/dist/core/agent-session.js.map +1 -1
  34. package/dist/core/agent-tool-settings.d.ts +35 -0
  35. package/dist/core/agent-tool-settings.d.ts.map +1 -0
  36. package/dist/core/agent-tool-settings.js +155 -0
  37. package/dist/core/agent-tool-settings.js.map +1 -0
  38. package/dist/core/atomic-session-file.d.ts +14 -0
  39. package/dist/core/atomic-session-file.d.ts.map +1 -0
  40. package/dist/core/atomic-session-file.js +104 -0
  41. package/dist/core/atomic-session-file.js.map +1 -0
  42. package/dist/core/compaction/compaction.d.ts +6 -0
  43. package/dist/core/compaction/compaction.d.ts.map +1 -1
  44. package/dist/core/compaction/compaction.js +12 -1
  45. package/dist/core/compaction/compaction.js.map +1 -1
  46. package/dist/core/compaction/hysteresis.d.ts +27 -0
  47. package/dist/core/compaction/hysteresis.d.ts.map +1 -0
  48. package/dist/core/compaction/hysteresis.js +84 -0
  49. package/dist/core/compaction/hysteresis.js.map +1 -0
  50. package/dist/core/compaction/index.d.ts +2 -0
  51. package/dist/core/compaction/index.d.ts.map +1 -1
  52. package/dist/core/compaction/index.js +2 -0
  53. package/dist/core/compaction/index.js.map +1 -1
  54. package/dist/core/compaction/transaction.d.ts +172 -0
  55. package/dist/core/compaction/transaction.d.ts.map +1 -0
  56. package/dist/core/compaction/transaction.js +824 -0
  57. package/dist/core/compaction/transaction.js.map +1 -0
  58. package/dist/core/context-budget-governor-v2.d.ts +2 -1
  59. package/dist/core/context-budget-governor-v2.d.ts.map +1 -1
  60. package/dist/core/context-budget-governor-v2.js +2 -1
  61. package/dist/core/context-budget-governor-v2.js.map +1 -1
  62. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  63. package/dist/core/context-budget-headroom.js +5 -1
  64. package/dist/core/context-budget-headroom.js.map +1 -1
  65. package/dist/core/context-budget-reserved-tokens.d.ts +110 -0
  66. package/dist/core/context-budget-reserved-tokens.d.ts.map +1 -0
  67. package/dist/core/context-budget-reserved-tokens.js +188 -0
  68. package/dist/core/context-budget-reserved-tokens.js.map +1 -0
  69. package/dist/core/context-budget-v2-cache-invalidation.d.ts +133 -0
  70. package/dist/core/context-budget-v2-cache-invalidation.d.ts.map +1 -0
  71. package/dist/core/context-budget-v2-cache-invalidation.js +452 -0
  72. package/dist/core/context-budget-v2-cache-invalidation.js.map +1 -0
  73. package/dist/core/context-budget-v2-cache-keys.d.ts +3 -0
  74. package/dist/core/context-budget-v2-cache-keys.d.ts.map +1 -1
  75. package/dist/core/context-budget-v2-cache-keys.js +6 -0
  76. package/dist/core/context-budget-v2-cache-keys.js.map +1 -1
  77. package/dist/core/context-budget-v2-cache-provider.d.ts +8 -2
  78. package/dist/core/context-budget-v2-cache-provider.d.ts.map +1 -1
  79. package/dist/core/context-budget-v2-cache-provider.js +28 -5
  80. package/dist/core/context-budget-v2-cache-provider.js.map +1 -1
  81. package/dist/core/context-budget-v2-plan-cache-keys.d.ts.map +1 -1
  82. package/dist/core/context-budget-v2-plan-cache-keys.js +1 -0
  83. package/dist/core/context-budget-v2-plan-cache-keys.js.map +1 -1
  84. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  85. package/dist/core/context-budget-v2-planner.js +1 -0
  86. package/dist/core/context-budget-v2-planner.js.map +1 -1
  87. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  88. package/dist/core/context-budget-v2-selection.js +9 -0
  89. package/dist/core/context-budget-v2-selection.js.map +1 -1
  90. package/dist/core/context-budget-v2-types.d.ts +4 -0
  91. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  92. package/dist/core/context-budget-v2-types.js.map +1 -1
  93. package/dist/core/durable-file-directory.d.ts +4 -0
  94. package/dist/core/durable-file-directory.d.ts.map +1 -0
  95. package/dist/core/durable-file-directory.js +76 -0
  96. package/dist/core/durable-file-directory.js.map +1 -0
  97. package/dist/core/durable-file-identity.d.ts +26 -0
  98. package/dist/core/durable-file-identity.d.ts.map +1 -0
  99. package/dist/core/durable-file-identity.js +118 -0
  100. package/dist/core/durable-file-identity.js.map +1 -0
  101. package/dist/core/durable-file-io.d.ts +29 -0
  102. package/dist/core/durable-file-io.d.ts.map +1 -0
  103. package/dist/core/durable-file-io.js +179 -0
  104. package/dist/core/durable-file-io.js.map +1 -0
  105. package/dist/core/durable-file-lock-cleanup.d.ts +12 -0
  106. package/dist/core/durable-file-lock-cleanup.d.ts.map +1 -0
  107. package/dist/core/durable-file-lock-cleanup.js +39 -0
  108. package/dist/core/durable-file-lock-cleanup.js.map +1 -0
  109. package/dist/core/durable-file-lock-owner.d.ts +18 -0
  110. package/dist/core/durable-file-lock-owner.d.ts.map +1 -0
  111. package/dist/core/durable-file-lock-owner.js +78 -0
  112. package/dist/core/durable-file-lock-owner.js.map +1 -0
  113. package/dist/core/durable-file-lock-storage.d.ts +35 -0
  114. package/dist/core/durable-file-lock-storage.d.ts.map +1 -0
  115. package/dist/core/durable-file-lock-storage.js +263 -0
  116. package/dist/core/durable-file-lock-storage.js.map +1 -0
  117. package/dist/core/durable-file-mode.d.ts +8 -0
  118. package/dist/core/durable-file-mode.d.ts.map +1 -0
  119. package/dist/core/durable-file-mode.js +41 -0
  120. package/dist/core/durable-file-mode.js.map +1 -0
  121. package/dist/core/extensions/loader.d.ts +12 -0
  122. package/dist/core/extensions/loader.d.ts.map +1 -1
  123. package/dist/core/extensions/loader.js +50 -37
  124. package/dist/core/extensions/loader.js.map +1 -1
  125. package/dist/core/extensions/types.d.ts +2 -0
  126. package/dist/core/extensions/types.d.ts.map +1 -1
  127. package/dist/core/extensions/types.js.map +1 -1
  128. package/dist/core/index.d.ts +6 -2
  129. package/dist/core/index.d.ts.map +1 -1
  130. package/dist/core/index.js +5 -0
  131. package/dist/core/index.js.map +1 -1
  132. package/dist/core/loop-gates.d.ts.map +1 -1
  133. package/dist/core/loop-gates.js +25 -1
  134. package/dist/core/loop-gates.js.map +1 -1
  135. package/dist/core/model-resolver.d.ts.map +1 -1
  136. package/dist/core/model-resolver.js +1 -1
  137. package/dist/core/model-resolver.js.map +1 -1
  138. package/dist/core/pi-package-intake.d.ts +1 -0
  139. package/dist/core/pi-package-intake.d.ts.map +1 -1
  140. package/dist/core/pi-package-intake.js +7 -0
  141. package/dist/core/pi-package-intake.js.map +1 -1
  142. package/dist/core/redaction.d.ts +8 -0
  143. package/dist/core/redaction.d.ts.map +1 -0
  144. package/dist/core/redaction.js +31 -0
  145. package/dist/core/redaction.js.map +1 -0
  146. package/dist/core/resource-loader.d.ts.map +1 -1
  147. package/dist/core/resource-loader.js +36 -0
  148. package/dist/core/resource-loader.js.map +1 -1
  149. package/dist/core/run-journal-store.d.ts +53 -0
  150. package/dist/core/run-journal-store.d.ts.map +1 -0
  151. package/dist/core/run-journal-store.js +278 -0
  152. package/dist/core/run-journal-store.js.map +1 -0
  153. package/dist/core/run-journal.d.ts +172 -0
  154. package/dist/core/run-journal.d.ts.map +1 -0
  155. package/dist/core/run-journal.js +1053 -0
  156. package/dist/core/run-journal.js.map +1 -0
  157. package/dist/core/sdk.d.ts +7 -0
  158. package/dist/core/sdk.d.ts.map +1 -1
  159. package/dist/core/sdk.js +94 -3
  160. package/dist/core/sdk.js.map +1 -1
  161. package/dist/core/session-doctor-plan.d.ts +128 -0
  162. package/dist/core/session-doctor-plan.d.ts.map +1 -0
  163. package/dist/core/session-doctor-plan.js +833 -0
  164. package/dist/core/session-doctor-plan.js.map +1 -0
  165. package/dist/core/session-file-compaction.d.ts +6 -0
  166. package/dist/core/session-file-compaction.d.ts.map +1 -0
  167. package/dist/core/session-file-compaction.js +169 -0
  168. package/dist/core/session-file-compaction.js.map +1 -0
  169. package/dist/core/session-file-head.d.ts +10 -0
  170. package/dist/core/session-file-head.d.ts.map +1 -0
  171. package/dist/core/session-file-head.js +78 -0
  172. package/dist/core/session-file-head.js.map +1 -0
  173. package/dist/core/session-file-persistence.d.ts +30 -0
  174. package/dist/core/session-file-persistence.d.ts.map +1 -0
  175. package/dist/core/session-file-persistence.js +106 -0
  176. package/dist/core/session-file-persistence.js.map +1 -0
  177. package/dist/core/session-integrity.d.ts +43 -0
  178. package/dist/core/session-integrity.d.ts.map +1 -0
  179. package/dist/core/session-integrity.js +420 -0
  180. package/dist/core/session-integrity.js.map +1 -0
  181. package/dist/core/session-manager.d.ts +24 -4
  182. package/dist/core/session-manager.d.ts.map +1 -1
  183. package/dist/core/session-manager.js +242 -125
  184. package/dist/core/session-manager.js.map +1 -1
  185. package/dist/core/session-owner-lease.d.ts +22 -0
  186. package/dist/core/session-owner-lease.d.ts.map +1 -0
  187. package/dist/core/session-owner-lease.js +176 -0
  188. package/dist/core/session-owner-lease.js.map +1 -0
  189. package/dist/core/session-path-policy.d.ts +132 -0
  190. package/dist/core/session-path-policy.d.ts.map +1 -0
  191. package/dist/core/session-path-policy.js +780 -0
  192. package/dist/core/session-path-policy.js.map +1 -0
  193. package/dist/core/session-repair-plan.d.ts +49 -0
  194. package/dist/core/session-repair-plan.d.ts.map +1 -0
  195. package/dist/core/session-repair-plan.js +202 -0
  196. package/dist/core/session-repair-plan.js.map +1 -0
  197. package/dist/core/session-termination.d.ts +100 -0
  198. package/dist/core/session-termination.d.ts.map +1 -0
  199. package/dist/core/session-termination.js +403 -0
  200. package/dist/core/session-termination.js.map +1 -0
  201. package/dist/core/settings-manager.d.ts +32 -0
  202. package/dist/core/settings-manager.d.ts.map +1 -1
  203. package/dist/core/settings-manager.js +52 -5
  204. package/dist/core/settings-manager.js.map +1 -1
  205. package/dist/core/system-prompt.d.ts.map +1 -1
  206. package/dist/core/system-prompt.js +1 -1
  207. package/dist/core/system-prompt.js.map +1 -1
  208. package/dist/core/verified-bash-adapter.d.ts +36 -0
  209. package/dist/core/verified-bash-adapter.d.ts.map +1 -0
  210. package/dist/core/verified-bash-adapter.js +90 -0
  211. package/dist/core/verified-bash-adapter.js.map +1 -0
  212. package/dist/guardrails/command-redaction.d.ts +32 -0
  213. package/dist/guardrails/command-redaction.d.ts.map +1 -0
  214. package/dist/guardrails/command-redaction.js +776 -0
  215. package/dist/guardrails/command-redaction.js.map +1 -0
  216. package/dist/guardrails/evidence-attestation.d.ts +31 -0
  217. package/dist/guardrails/evidence-attestation.d.ts.map +1 -0
  218. package/dist/guardrails/evidence-attestation.js +178 -0
  219. package/dist/guardrails/evidence-attestation.js.map +1 -0
  220. package/dist/guardrails/evidence-receipt-store.d.ts +33 -0
  221. package/dist/guardrails/evidence-receipt-store.d.ts.map +1 -0
  222. package/dist/guardrails/evidence-receipt-store.js +446 -0
  223. package/dist/guardrails/evidence-receipt-store.js.map +1 -0
  224. package/dist/guardrails/evidence-receipt.d.ts +58 -0
  225. package/dist/guardrails/evidence-receipt.d.ts.map +1 -0
  226. package/dist/guardrails/evidence-receipt.js +389 -0
  227. package/dist/guardrails/evidence-receipt.js.map +1 -0
  228. package/dist/guardrails/evidence-system.d.ts +40 -9
  229. package/dist/guardrails/evidence-system.d.ts.map +1 -1
  230. package/dist/guardrails/evidence-system.js +379 -83
  231. package/dist/guardrails/evidence-system.js.map +1 -1
  232. package/dist/guardrails/replay-ledger-io.d.ts +6 -0
  233. package/dist/guardrails/replay-ledger-io.d.ts.map +1 -0
  234. package/dist/guardrails/replay-ledger-io.js +42 -0
  235. package/dist/guardrails/replay-ledger-io.js.map +1 -0
  236. package/dist/guardrails/replay-ledger-lock-owner.d.ts +32 -0
  237. package/dist/guardrails/replay-ledger-lock-owner.d.ts.map +1 -0
  238. package/dist/guardrails/replay-ledger-lock-owner.js +138 -0
  239. package/dist/guardrails/replay-ledger-lock-owner.js.map +1 -0
  240. package/dist/guardrails/replay-ledger-lock.d.ts +24 -0
  241. package/dist/guardrails/replay-ledger-lock.d.ts.map +1 -0
  242. package/dist/guardrails/replay-ledger-lock.js +135 -0
  243. package/dist/guardrails/replay-ledger-lock.js.map +1 -0
  244. package/dist/guardrails/replay-ledger-mutation-gate.d.ts +24 -0
  245. package/dist/guardrails/replay-ledger-mutation-gate.d.ts.map +1 -0
  246. package/dist/guardrails/replay-ledger-mutation-gate.js +205 -0
  247. package/dist/guardrails/replay-ledger-mutation-gate.js.map +1 -0
  248. package/dist/guardrails/replay-ledger-quarantine.d.ts +2 -0
  249. package/dist/guardrails/replay-ledger-quarantine.d.ts.map +1 -0
  250. package/dist/guardrails/replay-ledger-quarantine.js +19 -0
  251. package/dist/guardrails/replay-ledger-quarantine.js.map +1 -0
  252. package/dist/guardrails/replay-ledger-store.d.ts +32 -0
  253. package/dist/guardrails/replay-ledger-store.d.ts.map +1 -0
  254. package/dist/guardrails/replay-ledger-store.js +211 -0
  255. package/dist/guardrails/replay-ledger-store.js.map +1 -0
  256. package/dist/guardrails/shell-command-name-glob.d.ts +3 -0
  257. package/dist/guardrails/shell-command-name-glob.d.ts.map +1 -0
  258. package/dist/guardrails/shell-command-name-glob.js +233 -0
  259. package/dist/guardrails/shell-command-name-glob.js.map +1 -0
  260. package/dist/guardrails/shell-command-option.d.ts +7 -0
  261. package/dist/guardrails/shell-command-option.d.ts.map +1 -0
  262. package/dist/guardrails/shell-command-option.js +24 -0
  263. package/dist/guardrails/shell-command-option.js.map +1 -0
  264. package/dist/guardrails/shell-command-tokenizer.d.ts +29 -0
  265. package/dist/guardrails/shell-command-tokenizer.d.ts.map +1 -0
  266. package/dist/guardrails/shell-command-tokenizer.js +145 -0
  267. package/dist/guardrails/shell-command-tokenizer.js.map +1 -0
  268. package/dist/guardrails/shell-command-wrapper.d.ts +18 -0
  269. package/dist/guardrails/shell-command-wrapper.d.ts.map +1 -0
  270. package/dist/guardrails/shell-command-wrapper.js +42 -0
  271. package/dist/guardrails/shell-command-wrapper.js.map +1 -0
  272. package/dist/guardrails/verified-executor.d.ts +65 -0
  273. package/dist/guardrails/verified-executor.d.ts.map +1 -0
  274. package/dist/guardrails/verified-executor.js +171 -0
  275. package/dist/guardrails/verified-executor.js.map +1 -0
  276. package/dist/guardrails/workspace-fingerprint.d.ts +23 -0
  277. package/dist/guardrails/workspace-fingerprint.d.ts.map +1 -0
  278. package/dist/guardrails/workspace-fingerprint.js +634 -0
  279. package/dist/guardrails/workspace-fingerprint.js.map +1 -0
  280. package/dist/index.d.ts +15 -3
  281. package/dist/index.d.ts.map +1 -1
  282. package/dist/index.js +13 -1
  283. package/dist/index.js.map +1 -1
  284. package/dist/main.d.ts.map +1 -1
  285. package/dist/main.js +12 -0
  286. package/dist/main.js.map +1 -1
  287. package/dist/modes/interactive/components/control-panel-layout.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/control-panel-layout.js +1 -1
  289. package/dist/modes/interactive/components/control-panel-layout.js.map +1 -1
  290. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  291. package/dist/modes/interactive/components/footer.js +1 -1
  292. package/dist/modes/interactive/components/footer.js.map +1 -1
  293. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  294. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  295. package/dist/modes/interactive/interactive-mode.js +59 -9
  296. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  297. package/dist/modes/print-mode.d.ts.map +1 -1
  298. package/dist/modes/print-mode.js +43 -2
  299. package/dist/modes/print-mode.js.map +1 -1
  300. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  301. package/dist/modes/rpc/rpc-mode.js +23 -5
  302. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  303. package/dist/modes/rpc/rpc-types.d.ts +3 -0
  304. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  305. package/dist/modes/rpc/rpc-types.js.map +1 -1
  306. package/dist/types/evidence.d.ts +215 -1
  307. package/dist/types/evidence.d.ts.map +1 -1
  308. package/dist/types/evidence.js.map +1 -1
  309. package/dist/verify-ci.d.ts +3 -0
  310. package/dist/verify-ci.d.ts.map +1 -0
  311. package/dist/verify-ci.js +30 -0
  312. package/dist/verify-ci.js.map +1 -0
  313. package/docs/compaction.md +44 -4
  314. package/docs/extensions.md +2 -0
  315. package/docs/providers.md +6 -0
  316. package/docs/sdk.md +238 -1
  317. package/docs/settings.md +67 -4
  318. package/docs/usage.md +38 -0
  319. package/examples/extensions/correctness-wall/README.md +6 -6
  320. package/examples/extensions/correctness-wall/index.ts +37 -11
  321. package/examples/extensions/custom-provider-anthropic/package-lock.json +5 -5
  322. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  323. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  324. package/examples/extensions/gondolin/package-lock.json +2 -2
  325. package/examples/extensions/gondolin/package.json +1 -1
  326. package/examples/extensions/sandbox/package-lock.json +2 -2
  327. package/examples/extensions/sandbox/package.json +1 -1
  328. package/examples/extensions/with-deps/package-lock.json +4 -4
  329. package/examples/extensions/with-deps/package.json +1 -1
  330. package/npm-shrinkwrap.json +12 -12
  331. package/package.json +4 -4
@@ -0,0 +1,211 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { closeSync, existsSync, constants as fsConstants, fstatSync, fsyncSync, ftruncateSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { fsyncDirectory, ReplayLedgerLock, writeAll } from "./replay-ledger-lock.js";
5
+ import { quarantineReplayLedgerSuffix } from "./replay-ledger-quarantine.js";
6
+ const GENESIS_HASH = "genesis";
7
+ export const EMPTY_REPLAY_LEDGER_HEAD = Object.freeze({
8
+ fileIdentity: null,
9
+ size: 0,
10
+ lastSeq: 0,
11
+ lastHash: GENESIS_HASH,
12
+ });
13
+ function identity(stat) {
14
+ return Object.freeze({ dev: String(stat.dev), ino: String(stat.ino) });
15
+ }
16
+ function sameIdentity(left, right) {
17
+ return left === null ? right === null : right !== null && left.dev === right.dev && left.ino === right.ino;
18
+ }
19
+ export function replayLedgerHeadsEqual(left, right) {
20
+ return (sameIdentity(left.fileIdentity, right.fileIdentity) &&
21
+ left.size === right.size &&
22
+ left.lastSeq === right.lastSeq &&
23
+ left.lastHash === right.lastHash);
24
+ }
25
+ function parseHead(value) {
26
+ if (typeof value !== "object" || value === null || Array.isArray(value))
27
+ throw new Error("replay committed head is invalid");
28
+ const record = value;
29
+ if (Object.keys(record).sort().join(",") !== "fileIdentity,lastHash,lastSeq,size") {
30
+ throw new Error("replay committed head has an invalid key set");
31
+ }
32
+ const rawIdentity = record.fileIdentity;
33
+ let fileIdentity = null;
34
+ if (rawIdentity !== null) {
35
+ if (typeof rawIdentity !== "object" || Array.isArray(rawIdentity))
36
+ throw new Error("replay file identity is invalid");
37
+ const fields = rawIdentity;
38
+ if (Object.keys(fields).sort().join(",") !== "dev,ino" ||
39
+ typeof fields.dev !== "string" ||
40
+ typeof fields.ino !== "string" ||
41
+ !/^\d+$/.test(fields.dev) ||
42
+ !/^\d+$/.test(fields.ino)) {
43
+ throw new Error("replay file identity is invalid");
44
+ }
45
+ fileIdentity = Object.freeze({ dev: fields.dev, ino: fields.ino });
46
+ }
47
+ if (!Number.isSafeInteger(record.size) ||
48
+ record.size < 0 ||
49
+ !Number.isSafeInteger(record.lastSeq) ||
50
+ record.lastSeq < 0 ||
51
+ typeof record.lastHash !== "string" ||
52
+ (record.lastSeq === 0 ? record.lastHash !== GENESIS_HASH : !/^[0-9a-f]{64}$/.test(record.lastHash))) {
53
+ throw new Error("replay committed head fields are invalid");
54
+ }
55
+ return Object.freeze({
56
+ fileIdentity,
57
+ size: record.size,
58
+ lastSeq: record.lastSeq,
59
+ lastHash: record.lastHash,
60
+ });
61
+ }
62
+ export class ReplayLedgerStore {
63
+ path;
64
+ inspect;
65
+ headPath;
66
+ lock;
67
+ afterLedgerFsync;
68
+ beforeQuarantineFsync;
69
+ constructor(path, inspect, options = {}) {
70
+ this.path = path;
71
+ this.inspect = inspect;
72
+ mkdirSync(dirname(path), { recursive: true });
73
+ this.headPath = `${path}.head`;
74
+ this.lock = new ReplayLedgerLock(`${path}.lock`);
75
+ this.afterLedgerFsync = options.afterLedgerFsync;
76
+ this.beforeQuarantineFsync = options.beforeQuarantineFsync;
77
+ }
78
+ withLock(operation) {
79
+ return this.lock.run(operation);
80
+ }
81
+ readVerified() {
82
+ const ledgerExists = existsSync(this.path);
83
+ const headExists = existsSync(this.headPath);
84
+ if (!ledgerExists && !headExists)
85
+ return { bytes: Buffer.alloc(0), head: EMPTY_REPLAY_LEDGER_HEAD };
86
+ if (ledgerExists && !headExists) {
87
+ const fd = openSync(this.path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
88
+ try {
89
+ const stat = fstatSync(fd);
90
+ if (!stat.isFile() || stat.size !== 0)
91
+ throw new Error("replay ledger or committed head suffix is missing");
92
+ }
93
+ finally {
94
+ closeSync(fd);
95
+ }
96
+ unlinkSync(this.path);
97
+ fsyncDirectory(dirname(this.path));
98
+ return { bytes: Buffer.alloc(0), head: EMPTY_REPLAY_LEDGER_HEAD };
99
+ }
100
+ if (!ledgerExists)
101
+ throw new Error("replay ledger or committed head suffix is missing");
102
+ const head = parseHead(JSON.parse(readFileSync(this.headPath, "utf8")));
103
+ const fd = openSync(this.path, fsConstants.O_RDWR | (fsConstants.O_NOFOLLOW ?? 0));
104
+ try {
105
+ const before = fstatSync(fd);
106
+ const bytes = readFileSync(fd);
107
+ const after = fstatSync(fd);
108
+ if (!before.isFile() ||
109
+ !after.isFile() ||
110
+ !sameIdentity(identity(before), identity(after)) ||
111
+ before.size !== after.size ||
112
+ after.size !== bytes.byteLength ||
113
+ !sameIdentity(head.fileIdentity, identity(after)) ||
114
+ head.size > after.size) {
115
+ throw new Error("replay ledger tampered: file identity or size does not match its committed head");
116
+ }
117
+ const committedBytes = head.size === after.size ? bytes : bytes.subarray(0, head.size);
118
+ const inspected = this.inspect(committedBytes);
119
+ if (inspected.lastSeq !== head.lastSeq || inspected.lastHash !== head.lastHash) {
120
+ throw new Error("replay ledger suffix does not match its committed head");
121
+ }
122
+ if (head.size < after.size) {
123
+ quarantineReplayLedgerSuffix(this.path, bytes.subarray(head.size), this.beforeQuarantineFsync);
124
+ ftruncateSync(fd, head.size);
125
+ fsyncSync(fd);
126
+ fsyncDirectory(dirname(this.path));
127
+ }
128
+ return { bytes: committedBytes, head };
129
+ }
130
+ finally {
131
+ closeSync(fd);
132
+ }
133
+ }
134
+ initializeGenesis() {
135
+ const fd = openSync(this.path, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);
136
+ try {
137
+ fsyncSync(fd);
138
+ const stat = fstatSync(fd);
139
+ const head = Object.freeze({ ...EMPTY_REPLAY_LEDGER_HEAD, fileIdentity: identity(stat) });
140
+ this.publishHead(head);
141
+ return head;
142
+ }
143
+ finally {
144
+ closeSync(fd);
145
+ }
146
+ }
147
+ publishHead(head) {
148
+ const temp = `${this.headPath}.${randomUUID()}.tmp`;
149
+ const fd = openSync(temp, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);
150
+ try {
151
+ writeAll(fd, Buffer.from(`${JSON.stringify(head)}\n`, "utf8"));
152
+ fsyncSync(fd);
153
+ }
154
+ finally {
155
+ closeSync(fd);
156
+ }
157
+ try {
158
+ renameSync(temp, this.headPath);
159
+ fsyncDirectory(dirname(this.path));
160
+ }
161
+ catch (error) {
162
+ if (existsSync(temp))
163
+ unlinkSync(temp);
164
+ throw error;
165
+ }
166
+ }
167
+ load(expectedHead) {
168
+ return this.withLock(() => {
169
+ const snapshot = this.readVerified();
170
+ if (expectedHead !== undefined && !replayLedgerHeadsEqual(snapshot.head, expectedHead)) {
171
+ throw new Error("replay ledger expected-head CAS verification failed");
172
+ }
173
+ return snapshot;
174
+ });
175
+ }
176
+ append(line, nextSeq, nextHash, expectedHead) {
177
+ return this.withLock(() => {
178
+ const snapshot = this.readVerified();
179
+ if (!replayLedgerHeadsEqual(snapshot.head, expectedHead)) {
180
+ throw new Error("replay ledger concurrent append failed expected-head CAS");
181
+ }
182
+ const appendHead = snapshot.head.fileIdentity === null ? this.initializeGenesis() : snapshot.head;
183
+ const fd = openSync(this.path, fsConstants.O_CREAT | fsConstants.O_APPEND | fsConstants.O_WRONLY | (fsConstants.O_NOFOLLOW ?? 0), 0o600);
184
+ try {
185
+ const before = fstatSync(fd);
186
+ if (before.size !== appendHead.size || !sameIdentity(appendHead.fileIdentity, identity(before))) {
187
+ throw new Error("replay ledger file identity or size changed before CAS append");
188
+ }
189
+ writeAll(fd, line);
190
+ fsyncSync(fd);
191
+ const after = fstatSync(fd);
192
+ if (!sameIdentity(identity(before), identity(after)) || after.size !== appendHead.size + line.byteLength) {
193
+ throw new Error("replay ledger file changed during CAS append");
194
+ }
195
+ const head = Object.freeze({
196
+ fileIdentity: identity(after),
197
+ size: after.size,
198
+ lastSeq: nextSeq,
199
+ lastHash: nextHash,
200
+ });
201
+ this.afterLedgerFsync?.();
202
+ this.publishHead(head);
203
+ return head;
204
+ }
205
+ finally {
206
+ closeSync(fd);
207
+ }
208
+ });
209
+ }
210
+ }
211
+ //# sourceMappingURL=replay-ledger-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-ledger-store.js","sourceRoot":"","sources":["../../src/guardrails/replay-ledger-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n\tcloseSync,\n\texistsSync,\n\tconstants as fsConstants,\n\tfstatSync,\n\tfsyncSync,\n\tftruncateSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\trenameSync,\n\tunlinkSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { ReplayLedgerHead } from \"../types/evidence.ts\";\nimport { fsyncDirectory, ReplayLedgerLock, writeAll } from \"./replay-ledger-lock.ts\";\nimport { quarantineReplayLedgerSuffix } from \"./replay-ledger-quarantine.ts\";\n\nconst GENESIS_HASH = \"genesis\";\nexport const EMPTY_REPLAY_LEDGER_HEAD: ReplayLedgerHead = Object.freeze({\n\tfileIdentity: null,\n\tsize: 0,\n\tlastSeq: 0,\n\tlastHash: GENESIS_HASH,\n});\ninterface InspectedLedger {\n\treadonly lastSeq: number;\n\treadonly lastHash: string;\n}\ninterface StoreSnapshot {\n\treadonly bytes: Buffer;\n\treadonly head: ReplayLedgerHead;\n}\nfunction identity(stat: ReturnType<typeof fstatSync>): ReplayLedgerHead[\"fileIdentity\"] {\n\treturn Object.freeze({ dev: String(stat.dev), ino: String(stat.ino) });\n}\n\nfunction sameIdentity(left: ReplayLedgerHead[\"fileIdentity\"], right: ReplayLedgerHead[\"fileIdentity\"]): boolean {\n\treturn left === null ? right === null : right !== null && left.dev === right.dev && left.ino === right.ino;\n}\n\nexport function replayLedgerHeadsEqual(left: ReplayLedgerHead, right: ReplayLedgerHead): boolean {\n\treturn (\n\t\tsameIdentity(left.fileIdentity, right.fileIdentity) &&\n\t\tleft.size === right.size &&\n\t\tleft.lastSeq === right.lastSeq &&\n\t\tleft.lastHash === right.lastHash\n\t);\n}\n\nfunction parseHead(value: unknown): ReplayLedgerHead {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value))\n\t\tthrow new Error(\"replay committed head is invalid\");\n\tconst record = value as Record<string, unknown>;\n\tif (Object.keys(record).sort().join(\",\") !== \"fileIdentity,lastHash,lastSeq,size\") {\n\t\tthrow new Error(\"replay committed head has an invalid key set\");\n\t}\n\tconst rawIdentity = record.fileIdentity;\n\tlet fileIdentity: ReplayLedgerHead[\"fileIdentity\"] = null;\n\tif (rawIdentity !== null) {\n\t\tif (typeof rawIdentity !== \"object\" || Array.isArray(rawIdentity))\n\t\t\tthrow new Error(\"replay file identity is invalid\");\n\t\tconst fields = rawIdentity as Record<string, unknown>;\n\t\tif (\n\t\t\tObject.keys(fields).sort().join(\",\") !== \"dev,ino\" ||\n\t\t\ttypeof fields.dev !== \"string\" ||\n\t\t\ttypeof fields.ino !== \"string\" ||\n\t\t\t!/^\\d+$/.test(fields.dev) ||\n\t\t\t!/^\\d+$/.test(fields.ino)\n\t\t) {\n\t\t\tthrow new Error(\"replay file identity is invalid\");\n\t\t}\n\t\tfileIdentity = Object.freeze({ dev: fields.dev, ino: fields.ino });\n\t}\n\tif (\n\t\t!Number.isSafeInteger(record.size) ||\n\t\t(record.size as number) < 0 ||\n\t\t!Number.isSafeInteger(record.lastSeq) ||\n\t\t(record.lastSeq as number) < 0 ||\n\t\ttypeof record.lastHash !== \"string\" ||\n\t\t(record.lastSeq === 0 ? record.lastHash !== GENESIS_HASH : !/^[0-9a-f]{64}$/.test(record.lastHash))\n\t) {\n\t\tthrow new Error(\"replay committed head fields are invalid\");\n\t}\n\treturn Object.freeze({\n\t\tfileIdentity,\n\t\tsize: record.size as number,\n\t\tlastSeq: record.lastSeq as number,\n\t\tlastHash: record.lastHash,\n\t});\n}\n\nexport interface ReplayLedgerStoreOptions {\n\treadonly afterLedgerFsync?: () => void;\n\treadonly beforeQuarantineFsync?: () => void;\n}\n\nexport class ReplayLedgerStore {\n\tprivate readonly path: string;\n\tprivate readonly inspect: (bytes: Buffer) => InspectedLedger;\n\tprivate readonly headPath: string;\n\tprivate readonly lock: ReplayLedgerLock;\n\tprivate readonly afterLedgerFsync: (() => void) | undefined;\n\tprivate readonly beforeQuarantineFsync: (() => void) | undefined;\n\n\tconstructor(path: string, inspect: (bytes: Buffer) => InspectedLedger, options: ReplayLedgerStoreOptions = {}) {\n\t\tthis.path = path;\n\t\tthis.inspect = inspect;\n\t\tmkdirSync(dirname(path), { recursive: true });\n\t\tthis.headPath = `${path}.head`;\n\t\tthis.lock = new ReplayLedgerLock(`${path}.lock`);\n\t\tthis.afterLedgerFsync = options.afterLedgerFsync;\n\t\tthis.beforeQuarantineFsync = options.beforeQuarantineFsync;\n\t}\n\n\tprivate withLock<T>(operation: () => T): T {\n\t\treturn this.lock.run(operation);\n\t}\n\n\tprivate readVerified(): StoreSnapshot {\n\t\tconst ledgerExists = existsSync(this.path);\n\t\tconst headExists = existsSync(this.headPath);\n\t\tif (!ledgerExists && !headExists) return { bytes: Buffer.alloc(0), head: EMPTY_REPLAY_LEDGER_HEAD };\n\t\tif (ledgerExists && !headExists) {\n\t\t\tconst fd = openSync(this.path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));\n\t\t\ttry {\n\t\t\t\tconst stat = fstatSync(fd);\n\t\t\t\tif (!stat.isFile() || stat.size !== 0) throw new Error(\"replay ledger or committed head suffix is missing\");\n\t\t\t} finally {\n\t\t\t\tcloseSync(fd);\n\t\t\t}\n\t\t\tunlinkSync(this.path);\n\t\t\tfsyncDirectory(dirname(this.path));\n\t\t\treturn { bytes: Buffer.alloc(0), head: EMPTY_REPLAY_LEDGER_HEAD };\n\t\t}\n\t\tif (!ledgerExists) throw new Error(\"replay ledger or committed head suffix is missing\");\n\t\tconst head = parseHead(JSON.parse(readFileSync(this.headPath, \"utf8\")));\n\t\tconst fd = openSync(this.path, fsConstants.O_RDWR | (fsConstants.O_NOFOLLOW ?? 0));\n\t\ttry {\n\t\t\tconst before = fstatSync(fd);\n\t\t\tconst bytes = readFileSync(fd);\n\t\t\tconst after = fstatSync(fd);\n\t\t\tif (\n\t\t\t\t!before.isFile() ||\n\t\t\t\t!after.isFile() ||\n\t\t\t\t!sameIdentity(identity(before), identity(after)) ||\n\t\t\t\tbefore.size !== after.size ||\n\t\t\t\tafter.size !== bytes.byteLength ||\n\t\t\t\t!sameIdentity(head.fileIdentity, identity(after)) ||\n\t\t\t\thead.size > after.size\n\t\t\t) {\n\t\t\t\tthrow new Error(\"replay ledger tampered: file identity or size does not match its committed head\");\n\t\t\t}\n\t\t\tconst committedBytes = head.size === after.size ? bytes : bytes.subarray(0, head.size);\n\t\t\tconst inspected = this.inspect(committedBytes);\n\t\t\tif (inspected.lastSeq !== head.lastSeq || inspected.lastHash !== head.lastHash) {\n\t\t\t\tthrow new Error(\"replay ledger suffix does not match its committed head\");\n\t\t\t}\n\t\t\tif (head.size < after.size) {\n\t\t\t\tquarantineReplayLedgerSuffix(this.path, bytes.subarray(head.size), this.beforeQuarantineFsync);\n\t\t\t\tftruncateSync(fd, head.size);\n\t\t\t\tfsyncSync(fd);\n\t\t\t\tfsyncDirectory(dirname(this.path));\n\t\t\t}\n\t\t\treturn { bytes: committedBytes, head };\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tprivate initializeGenesis(): ReplayLedgerHead {\n\t\tconst fd = openSync(this.path, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);\n\t\ttry {\n\t\t\tfsyncSync(fd);\n\t\t\tconst stat = fstatSync(fd);\n\t\t\tconst head = Object.freeze({ ...EMPTY_REPLAY_LEDGER_HEAD, fileIdentity: identity(stat) });\n\t\t\tthis.publishHead(head);\n\t\t\treturn head;\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n\n\tprivate publishHead(head: ReplayLedgerHead): void {\n\t\tconst temp = `${this.headPath}.${randomUUID()}.tmp`;\n\t\tconst fd = openSync(temp, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);\n\t\ttry {\n\t\t\twriteAll(fd, Buffer.from(`${JSON.stringify(head)}\\n`, \"utf8\"));\n\t\t\tfsyncSync(fd);\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t\ttry {\n\t\t\trenameSync(temp, this.headPath);\n\t\t\tfsyncDirectory(dirname(this.path));\n\t\t} catch (error) {\n\t\t\tif (existsSync(temp)) unlinkSync(temp);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tload(expectedHead?: ReplayLedgerHead): StoreSnapshot {\n\t\treturn this.withLock(() => {\n\t\t\tconst snapshot = this.readVerified();\n\t\t\tif (expectedHead !== undefined && !replayLedgerHeadsEqual(snapshot.head, expectedHead)) {\n\t\t\t\tthrow new Error(\"replay ledger expected-head CAS verification failed\");\n\t\t\t}\n\t\t\treturn snapshot;\n\t\t});\n\t}\n\n\tappend(line: Buffer, nextSeq: number, nextHash: string, expectedHead: ReplayLedgerHead): ReplayLedgerHead {\n\t\treturn this.withLock(() => {\n\t\t\tconst snapshot = this.readVerified();\n\t\t\tif (!replayLedgerHeadsEqual(snapshot.head, expectedHead)) {\n\t\t\t\tthrow new Error(\"replay ledger concurrent append failed expected-head CAS\");\n\t\t\t}\n\t\t\tconst appendHead = snapshot.head.fileIdentity === null ? this.initializeGenesis() : snapshot.head;\n\t\t\tconst fd = openSync(\n\t\t\t\tthis.path,\n\t\t\t\tfsConstants.O_CREAT | fsConstants.O_APPEND | fsConstants.O_WRONLY | (fsConstants.O_NOFOLLOW ?? 0),\n\t\t\t\t0o600,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst before = fstatSync(fd);\n\t\t\t\tif (before.size !== appendHead.size || !sameIdentity(appendHead.fileIdentity, identity(before))) {\n\t\t\t\t\tthrow new Error(\"replay ledger file identity or size changed before CAS append\");\n\t\t\t\t}\n\t\t\t\twriteAll(fd, line);\n\t\t\t\tfsyncSync(fd);\n\t\t\t\tconst after = fstatSync(fd);\n\t\t\t\tif (!sameIdentity(identity(before), identity(after)) || after.size !== appendHead.size + line.byteLength) {\n\t\t\t\t\tthrow new Error(\"replay ledger file changed during CAS append\");\n\t\t\t\t}\n\t\t\t\tconst head = Object.freeze({\n\t\t\t\t\tfileIdentity: identity(after),\n\t\t\t\t\tsize: after.size,\n\t\t\t\t\tlastSeq: nextSeq,\n\t\t\t\t\tlastHash: nextHash,\n\t\t\t\t});\n\t\t\t\tthis.afterLedgerFsync?.();\n\t\t\t\tthis.publishHead(head);\n\t\t\t\treturn head;\n\t\t\t} finally {\n\t\t\t\tcloseSync(fd);\n\t\t\t}\n\t\t});\n\t}\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import { type NormalizedStaticShell } from "./shell-command-tokenizer.ts";
2
+ export declare function hasActiveShellNameGlob(shell: NormalizedStaticShell): boolean;
3
+ //# sourceMappingURL=shell-command-name-glob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-command-name-glob.d.ts","sourceRoot":"","sources":["../../src/guardrails/shell-command-name-glob.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,qBAAqB,EAAwB,MAAM,8BAA8B,CAAC;AA6HhG,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAyH5E","sourcesContent":["import { curlValue, ENV_VALUE_OPTIONS } from \"./shell-command-option.ts\";\nimport { type NormalizedStaticShell, normalizeStaticShell } from \"./shell-command-tokenizer.ts\";\nimport {\n\tconsumeShellCommandWrapper,\n\ttype ShellCommandWrapperState,\n\tshellCommandWrapper,\n} from \"./shell-command-wrapper.ts\";\n\nfunction activeSource(shell: NormalizedStaticShell, index: number): boolean {\n\tconst source = shell.sources[index];\n\treturn source !== undefined && !source.escaped && source.quoteStart === undefined;\n}\n\nfunction bracketEnd(shell: NormalizedStaticShell, start: number, end: number): number | undefined {\n\tif (shell.text[start] !== \"[\" || !activeSource(shell, start)) return undefined;\n\tlet cursor = start + 1;\n\tif (cursor < end && (shell.text[cursor] === \"!\" || shell.text[cursor] === \"^\") && activeSource(shell, cursor)) {\n\t\tcursor++;\n\t}\n\tlet members = 0;\n\tif (cursor < end && shell.text[cursor] === \"]\" && activeSource(shell, cursor)) {\n\t\tmembers++;\n\t\tcursor++;\n\t}\n\twhile (cursor < end) {\n\t\tif (shell.text[cursor] === \"]\" && activeSource(shell, cursor) && members > 0) return cursor;\n\t\tif (\n\t\t\tshell.text[cursor] === \"[\" &&\n\t\t\tactiveSource(shell, cursor) &&\n\t\t\t\".=:\".includes(shell.text[cursor + 1] ?? \"\") &&\n\t\t\tactiveSource(shell, cursor + 1)\n\t\t) {\n\t\t\tconst terminator = `${shell.text[cursor + 1]}]`;\n\t\t\tconst nestedEnd = shell.text.indexOf(terminator, cursor + 2);\n\t\t\tif (nestedEnd >= 0 && nestedEnd + 1 < end && activeSource(shell, nestedEnd + 1)) {\n\t\t\t\tmembers++;\n\t\t\t\tcursor = nestedEnd + 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tmembers++;\n\t\tcursor++;\n\t}\n\treturn undefined;\n}\n\nfunction activeGlob(shell: NormalizedStaticShell, start: number, end: number): boolean {\n\tfor (let index = start; index < end; index++) {\n\t\tif (!activeSource(shell, index)) continue;\n\t\tconst character = shell.text[index];\n\t\tif (character === \"*\" || character === \"?\") return true;\n\t\tconst close = bracketEnd(shell, index, end);\n\t\tif (close !== undefined) return true;\n\t}\n\treturn false;\n}\n\nfunction separator(shell: NormalizedStaticShell, start: number, end: number, delimiters: string): number {\n\tfor (let index = start; index < end; index++) {\n\t\tconst close = bracketEnd(shell, index, end);\n\t\tif (close !== undefined) {\n\t\t\tindex = close;\n\t\t\tcontinue;\n\t\t}\n\t\tif (delimiters.includes(shell.text[index])) return index;\n\t}\n\treturn end;\n}\n\nfunction previousArgument(shell: NormalizedStaticShell, index: number): string | undefined {\n\tconst current = shell.words[index];\n\tconst previous = shell.words[index - 1];\n\treturn current !== undefined && previous !== undefined && /^\\s*$/.test(shell.text.slice(previous.end, current.start))\n\t\t? shell.text.slice(previous.start, previous.end)\n\t\t: undefined;\n}\n\nfunction nameLike(shell: NormalizedStaticShell, start: number, end: number, allowLeadingQuestion: boolean): boolean {\n\tconst first = shell.text[start];\n\tif (\n\t\tfirst === undefined ||\n\t\t(!/[A-Za-z_*_]/.test(first) && first !== \"[\" && !(allowLeadingQuestion && first === \"?\"))\n\t) {\n\t\treturn false;\n\t}\n\tfor (let index = start; index < end; index++) {\n\t\tconst close = bracketEnd(shell, index, end);\n\t\tif (close !== undefined) {\n\t\t\tindex = close;\n\t\t\tcontinue;\n\t\t}\n\t\tif (\"/:.\".includes(shell.text[index])) return false;\n\t}\n\treturn true;\n}\n\nfunction hasActiveQueryNameGlob(\n\tshell: NormalizedStaticShell,\n\twordStart: number,\n\twordEnd: number,\n\tcommandTreatsArgumentsAsUrls: boolean,\n): boolean {\n\tconst text = shell.text.slice(wordStart, wordEnd);\n\tconst question = text.indexOf(\"?\");\n\tif (question < 0) return false;\n\tconst markerStart = wordStart + question;\n\tconst hasQuerySeparator = separator(shell, markerStart + 1, wordEnd, \"=&#\") < wordEnd;\n\tconst structuralQuery = hasQuerySeparator && (question === 0 || text.slice(0, question).startsWith(\"/\"));\n\tif (!commandTreatsArgumentsAsUrls && !text.slice(0, question).includes(\"://\") && !structuralQuery) return false;\n\tlet marker = markerStart;\n\twhile (marker < wordEnd) {\n\t\tconst nameStart = marker + 1;\n\t\tconst nameEnd = separator(shell, nameStart, wordEnd, \"=&#\");\n\t\tif (activeGlob(shell, nameStart, nameEnd)) return true;\n\t\tif (nameEnd >= wordEnd || shell.text[nameEnd] === \"#\") return false;\n\t\tif (shell.text[nameEnd] === \"&\") {\n\t\t\tmarker = nameEnd;\n\t\t\tcontinue;\n\t\t}\n\t\tconst next = separator(shell, nameEnd + 1, wordEnd, \"&#\");\n\t\tif (next >= wordEnd || shell.text[next] === \"#\") return false;\n\t\tmarker = next;\n\t}\n\treturn false;\n}\n\nexport function hasActiveShellNameGlob(shell: NormalizedStaticShell): boolean {\n\tlet optionsEnded = false;\n\tlet commandName: string | undefined;\n\tlet commandWord = -1;\n\tlet wrapper: ShellCommandWrapperState | undefined;\n\tfor (let index = 0; index < shell.words.length; index++) {\n\t\tconst word = shell.words[index];\n\t\tconst previousWord = shell.words[index - 1];\n\t\tif (previousWord !== undefined && /[;&|()\\n]/.test(shell.text.slice(previousWord.end, word.start))) {\n\t\t\tif (wrapper !== undefined) return true;\n\t\t\toptionsEnded = false;\n\t\t\tcommandName = undefined;\n\t\t\twrapper = undefined;\n\t\t}\n\t\tconst text = shell.text.slice(word.start, word.end);\n\t\tconst previous = previousArgument(shell, index);\n\t\tconst assignment = separator(shell, word.start, word.end, \"=\");\n\t\tconst commandWasUnset = commandName === undefined;\n\t\tconst staticAssignment =\n\t\t\tassignment < word.end &&\n\t\t\tnameLike(shell, word.start, assignment, false) &&\n\t\t\t!activeGlob(shell, word.start, assignment);\n\t\tif (commandWasUnset && !staticAssignment) {\n\t\t\tcommandName = text;\n\t\t\tcommandWord = index;\n\t\t\twrapper = shellCommandWrapper(text);\n\t\t}\n\t\tlet wrapperOptionEnd: number | undefined;\n\t\tlet wrapperValue = false;\n\t\tif (wrapper !== undefined && index !== commandWord) {\n\t\t\tconst step = consumeShellCommandWrapper(wrapper, text);\n\t\t\tif (step.ambiguous) return true;\n\t\t\twrapper = step.state;\n\t\t\twrapperOptionEnd = step.optionEnd;\n\t\t\twrapperValue = step.value === true;\n\t\t\tif (step.target) {\n\t\t\t\tcommandName = text;\n\t\t\t\tcommandWord = index;\n\t\t\t\toptionsEnded = false;\n\t\t\t\twrapper = shellCommandWrapper(text);\n\t\t\t}\n\t\t}\n\t\tlet envCommand = commandName !== undefined && /(?:^|\\/)env(?:\\.exe)?$/.test(commandName);\n\t\tconst envAssignment = assignment < word.end && nameLike(shell, word.start, assignment, true);\n\t\tconst envOptionValue = envCommand && previous !== undefined && ENV_VALUE_OPTIONS.has(previous);\n\t\tif (envCommand && index !== commandWord && !text.startsWith(\"-\") && !envAssignment && !envOptionValue) {\n\t\t\tcommandName = text;\n\t\t\tcommandWord = index;\n\t\t\toptionsEnded = false;\n\t\t\twrapper = shellCommandWrapper(text);\n\t\t\tenvCommand = false;\n\t\t}\n\t\tconst curlCommand = commandName !== undefined && /(?:^|\\/)curl(?:\\.exe)?$/.test(commandName);\n\t\tconst currentCurlValue = !optionsEnded && curlCommand ? curlValue(text) : undefined;\n\t\tconst previousCurlValue =\n\t\t\t!optionsEnded && curlCommand && previous !== undefined ? curlValue(previous) : undefined;\n\t\tconst valueFromPrevious = previousCurlValue !== undefined && previousCurlValue.offset === previous?.length;\n\t\tconst fileValue =\n\t\t\t(currentCurlValue?.kind === \"file\" && currentCurlValue.offset < text.length) ||\n\t\t\t(valueFromPrevious && previousCurlValue?.kind === \"file\");\n\t\tconst headerValue =\n\t\t\t(currentCurlValue?.kind === \"header\" && currentCurlValue.offset < text.length) ||\n\t\t\t(valueFromPrevious && previousCurlValue?.kind === \"header\");\n\t\tconst optionValue = fileValue || headerValue || envOptionValue || wrapperValue;\n\t\tif (!optionsEnded && !optionValue && text === \"--\") {\n\t\t\toptionsEnded = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst optionPrefix =\n\t\t\t!optionsEnded && !valueFromPrevious && !envOptionValue && !wrapperValue\n\t\t\t\t? /^--?/.exec(text)?.[0].length\n\t\t\t\t: undefined;\n\t\tif (optionPrefix !== undefined) {\n\t\t\tconst optionEnd =\n\t\t\t\twrapperOptionEnd ??\n\t\t\t\tcurrentCurlValue?.offset ??\n\t\t\t\tseparator(shell, word.start + optionPrefix, word.end, \"=\") - word.start;\n\t\t\tif (activeGlob(shell, word.start + optionPrefix, word.start + optionEnd)) return true;\n\t\t}\n\t\tconst shellCommand = commandName !== undefined && /(?:^|\\/)(?:ba|da|k|z)?sh(?:\\.exe)?$/i.test(commandName);\n\t\tif (shellCommand && index !== commandWord && /^-[A-Za-z]*c[A-Za-z]*$/.test(text)) {\n\t\t\tconst script = previousArgument(shell, index + 1) === text ? shell.words[index + 1] : undefined;\n\t\t\tif (script !== undefined) normalizeStaticShell(shell.text.slice(script.start, script.end));\n\t\t}\n\t\tif (commandName !== undefined && /(?:^|\\/)eval$/.test(commandName) && index === commandWord + 1) {\n\t\t\tlet last = index;\n\t\t\twhile (\n\t\t\t\tpreviousArgument(shell, last + 1) === shell.text.slice(shell.words[last].start, shell.words[last].end)\n\t\t\t) {\n\t\t\t\tlast++;\n\t\t\t}\n\t\t\tnormalizeStaticShell(shell.text.slice(word.start, shell.words[last].end));\n\t\t}\n\t\tif (\n\t\t\t!fileValue &&\n\t\t\t!headerValue &&\n\t\t\thasActiveQueryNameGlob(shell, word.start, word.end, index !== commandWord && curlCommand)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tconst explicitEnvironment = index !== commandWord && (envCommand || commandName === \"export\");\n\t\tif (\n\t\t\t(commandWasUnset || explicitEnvironment) &&\n\t\t\tassignment < word.end &&\n\t\t\t(!text.startsWith(\"?\") || explicitEnvironment) &&\n\t\t\tnameLike(shell, word.start, assignment, explicitEnvironment) &&\n\t\t\tactiveGlob(shell, word.start, assignment)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tconst headerStart = headerValue\n\t\t\t? currentCurlValue?.kind === \"header\"\n\t\t\t\t? word.start + currentCurlValue.offset\n\t\t\t\t: word.start\n\t\t\t: undefined;\n\t\tif (headerStart !== undefined) {\n\t\t\tconst headerEnd = separator(shell, headerStart, word.end, \":=\");\n\t\t\tif (activeGlob(shell, headerStart, headerEnd)) return true;\n\t\t}\n\t}\n\treturn wrapper !== undefined;\n}\n"]}
@@ -0,0 +1,233 @@
1
+ import { curlValue, ENV_VALUE_OPTIONS } from "./shell-command-option.js";
2
+ import { normalizeStaticShell } from "./shell-command-tokenizer.js";
3
+ import { consumeShellCommandWrapper, shellCommandWrapper, } from "./shell-command-wrapper.js";
4
+ function activeSource(shell, index) {
5
+ const source = shell.sources[index];
6
+ return source !== undefined && !source.escaped && source.quoteStart === undefined;
7
+ }
8
+ function bracketEnd(shell, start, end) {
9
+ if (shell.text[start] !== "[" || !activeSource(shell, start))
10
+ return undefined;
11
+ let cursor = start + 1;
12
+ if (cursor < end && (shell.text[cursor] === "!" || shell.text[cursor] === "^") && activeSource(shell, cursor)) {
13
+ cursor++;
14
+ }
15
+ let members = 0;
16
+ if (cursor < end && shell.text[cursor] === "]" && activeSource(shell, cursor)) {
17
+ members++;
18
+ cursor++;
19
+ }
20
+ while (cursor < end) {
21
+ if (shell.text[cursor] === "]" && activeSource(shell, cursor) && members > 0)
22
+ return cursor;
23
+ if (shell.text[cursor] === "[" &&
24
+ activeSource(shell, cursor) &&
25
+ ".=:".includes(shell.text[cursor + 1] ?? "") &&
26
+ activeSource(shell, cursor + 1)) {
27
+ const terminator = `${shell.text[cursor + 1]}]`;
28
+ const nestedEnd = shell.text.indexOf(terminator, cursor + 2);
29
+ if (nestedEnd >= 0 && nestedEnd + 1 < end && activeSource(shell, nestedEnd + 1)) {
30
+ members++;
31
+ cursor = nestedEnd + 2;
32
+ continue;
33
+ }
34
+ }
35
+ members++;
36
+ cursor++;
37
+ }
38
+ return undefined;
39
+ }
40
+ function activeGlob(shell, start, end) {
41
+ for (let index = start; index < end; index++) {
42
+ if (!activeSource(shell, index))
43
+ continue;
44
+ const character = shell.text[index];
45
+ if (character === "*" || character === "?")
46
+ return true;
47
+ const close = bracketEnd(shell, index, end);
48
+ if (close !== undefined)
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ function separator(shell, start, end, delimiters) {
54
+ for (let index = start; index < end; index++) {
55
+ const close = bracketEnd(shell, index, end);
56
+ if (close !== undefined) {
57
+ index = close;
58
+ continue;
59
+ }
60
+ if (delimiters.includes(shell.text[index]))
61
+ return index;
62
+ }
63
+ return end;
64
+ }
65
+ function previousArgument(shell, index) {
66
+ const current = shell.words[index];
67
+ const previous = shell.words[index - 1];
68
+ return current !== undefined && previous !== undefined && /^\s*$/.test(shell.text.slice(previous.end, current.start))
69
+ ? shell.text.slice(previous.start, previous.end)
70
+ : undefined;
71
+ }
72
+ function nameLike(shell, start, end, allowLeadingQuestion) {
73
+ const first = shell.text[start];
74
+ if (first === undefined ||
75
+ (!/[A-Za-z_*_]/.test(first) && first !== "[" && !(allowLeadingQuestion && first === "?"))) {
76
+ return false;
77
+ }
78
+ for (let index = start; index < end; index++) {
79
+ const close = bracketEnd(shell, index, end);
80
+ if (close !== undefined) {
81
+ index = close;
82
+ continue;
83
+ }
84
+ if ("/:.".includes(shell.text[index]))
85
+ return false;
86
+ }
87
+ return true;
88
+ }
89
+ function hasActiveQueryNameGlob(shell, wordStart, wordEnd, commandTreatsArgumentsAsUrls) {
90
+ const text = shell.text.slice(wordStart, wordEnd);
91
+ const question = text.indexOf("?");
92
+ if (question < 0)
93
+ return false;
94
+ const markerStart = wordStart + question;
95
+ const hasQuerySeparator = separator(shell, markerStart + 1, wordEnd, "=&#") < wordEnd;
96
+ const structuralQuery = hasQuerySeparator && (question === 0 || text.slice(0, question).startsWith("/"));
97
+ if (!commandTreatsArgumentsAsUrls && !text.slice(0, question).includes("://") && !structuralQuery)
98
+ return false;
99
+ let marker = markerStart;
100
+ while (marker < wordEnd) {
101
+ const nameStart = marker + 1;
102
+ const nameEnd = separator(shell, nameStart, wordEnd, "=&#");
103
+ if (activeGlob(shell, nameStart, nameEnd))
104
+ return true;
105
+ if (nameEnd >= wordEnd || shell.text[nameEnd] === "#")
106
+ return false;
107
+ if (shell.text[nameEnd] === "&") {
108
+ marker = nameEnd;
109
+ continue;
110
+ }
111
+ const next = separator(shell, nameEnd + 1, wordEnd, "&#");
112
+ if (next >= wordEnd || shell.text[next] === "#")
113
+ return false;
114
+ marker = next;
115
+ }
116
+ return false;
117
+ }
118
+ export function hasActiveShellNameGlob(shell) {
119
+ let optionsEnded = false;
120
+ let commandName;
121
+ let commandWord = -1;
122
+ let wrapper;
123
+ for (let index = 0; index < shell.words.length; index++) {
124
+ const word = shell.words[index];
125
+ const previousWord = shell.words[index - 1];
126
+ if (previousWord !== undefined && /[;&|()\n]/.test(shell.text.slice(previousWord.end, word.start))) {
127
+ if (wrapper !== undefined)
128
+ return true;
129
+ optionsEnded = false;
130
+ commandName = undefined;
131
+ wrapper = undefined;
132
+ }
133
+ const text = shell.text.slice(word.start, word.end);
134
+ const previous = previousArgument(shell, index);
135
+ const assignment = separator(shell, word.start, word.end, "=");
136
+ const commandWasUnset = commandName === undefined;
137
+ const staticAssignment = assignment < word.end &&
138
+ nameLike(shell, word.start, assignment, false) &&
139
+ !activeGlob(shell, word.start, assignment);
140
+ if (commandWasUnset && !staticAssignment) {
141
+ commandName = text;
142
+ commandWord = index;
143
+ wrapper = shellCommandWrapper(text);
144
+ }
145
+ let wrapperOptionEnd;
146
+ let wrapperValue = false;
147
+ if (wrapper !== undefined && index !== commandWord) {
148
+ const step = consumeShellCommandWrapper(wrapper, text);
149
+ if (step.ambiguous)
150
+ return true;
151
+ wrapper = step.state;
152
+ wrapperOptionEnd = step.optionEnd;
153
+ wrapperValue = step.value === true;
154
+ if (step.target) {
155
+ commandName = text;
156
+ commandWord = index;
157
+ optionsEnded = false;
158
+ wrapper = shellCommandWrapper(text);
159
+ }
160
+ }
161
+ let envCommand = commandName !== undefined && /(?:^|\/)env(?:\.exe)?$/.test(commandName);
162
+ const envAssignment = assignment < word.end && nameLike(shell, word.start, assignment, true);
163
+ const envOptionValue = envCommand && previous !== undefined && ENV_VALUE_OPTIONS.has(previous);
164
+ if (envCommand && index !== commandWord && !text.startsWith("-") && !envAssignment && !envOptionValue) {
165
+ commandName = text;
166
+ commandWord = index;
167
+ optionsEnded = false;
168
+ wrapper = shellCommandWrapper(text);
169
+ envCommand = false;
170
+ }
171
+ const curlCommand = commandName !== undefined && /(?:^|\/)curl(?:\.exe)?$/.test(commandName);
172
+ const currentCurlValue = !optionsEnded && curlCommand ? curlValue(text) : undefined;
173
+ const previousCurlValue = !optionsEnded && curlCommand && previous !== undefined ? curlValue(previous) : undefined;
174
+ const valueFromPrevious = previousCurlValue !== undefined && previousCurlValue.offset === previous?.length;
175
+ const fileValue = (currentCurlValue?.kind === "file" && currentCurlValue.offset < text.length) ||
176
+ (valueFromPrevious && previousCurlValue?.kind === "file");
177
+ const headerValue = (currentCurlValue?.kind === "header" && currentCurlValue.offset < text.length) ||
178
+ (valueFromPrevious && previousCurlValue?.kind === "header");
179
+ const optionValue = fileValue || headerValue || envOptionValue || wrapperValue;
180
+ if (!optionsEnded && !optionValue && text === "--") {
181
+ optionsEnded = true;
182
+ continue;
183
+ }
184
+ const optionPrefix = !optionsEnded && !valueFromPrevious && !envOptionValue && !wrapperValue
185
+ ? /^--?/.exec(text)?.[0].length
186
+ : undefined;
187
+ if (optionPrefix !== undefined) {
188
+ const optionEnd = wrapperOptionEnd ??
189
+ currentCurlValue?.offset ??
190
+ separator(shell, word.start + optionPrefix, word.end, "=") - word.start;
191
+ if (activeGlob(shell, word.start + optionPrefix, word.start + optionEnd))
192
+ return true;
193
+ }
194
+ const shellCommand = commandName !== undefined && /(?:^|\/)(?:ba|da|k|z)?sh(?:\.exe)?$/i.test(commandName);
195
+ if (shellCommand && index !== commandWord && /^-[A-Za-z]*c[A-Za-z]*$/.test(text)) {
196
+ const script = previousArgument(shell, index + 1) === text ? shell.words[index + 1] : undefined;
197
+ if (script !== undefined)
198
+ normalizeStaticShell(shell.text.slice(script.start, script.end));
199
+ }
200
+ if (commandName !== undefined && /(?:^|\/)eval$/.test(commandName) && index === commandWord + 1) {
201
+ let last = index;
202
+ while (previousArgument(shell, last + 1) === shell.text.slice(shell.words[last].start, shell.words[last].end)) {
203
+ last++;
204
+ }
205
+ normalizeStaticShell(shell.text.slice(word.start, shell.words[last].end));
206
+ }
207
+ if (!fileValue &&
208
+ !headerValue &&
209
+ hasActiveQueryNameGlob(shell, word.start, word.end, index !== commandWord && curlCommand)) {
210
+ return true;
211
+ }
212
+ const explicitEnvironment = index !== commandWord && (envCommand || commandName === "export");
213
+ if ((commandWasUnset || explicitEnvironment) &&
214
+ assignment < word.end &&
215
+ (!text.startsWith("?") || explicitEnvironment) &&
216
+ nameLike(shell, word.start, assignment, explicitEnvironment) &&
217
+ activeGlob(shell, word.start, assignment)) {
218
+ return true;
219
+ }
220
+ const headerStart = headerValue
221
+ ? currentCurlValue?.kind === "header"
222
+ ? word.start + currentCurlValue.offset
223
+ : word.start
224
+ : undefined;
225
+ if (headerStart !== undefined) {
226
+ const headerEnd = separator(shell, headerStart, word.end, ":=");
227
+ if (activeGlob(shell, headerStart, headerEnd))
228
+ return true;
229
+ }
230
+ }
231
+ return wrapper !== undefined;
232
+ }
233
+ //# sourceMappingURL=shell-command-name-glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-command-name-glob.js","sourceRoot":"","sources":["../../src/guardrails/shell-command-name-glob.ts"],"names":[],"mappings":"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","sourcesContent":["import { curlValue, ENV_VALUE_OPTIONS } from \"./shell-command-option.ts\";\nimport { type NormalizedStaticShell, normalizeStaticShell } from \"./shell-command-tokenizer.ts\";\nimport {\n\tconsumeShellCommandWrapper,\n\ttype ShellCommandWrapperState,\n\tshellCommandWrapper,\n} from \"./shell-command-wrapper.ts\";\n\nfunction activeSource(shell: NormalizedStaticShell, index: number): boolean {\n\tconst source = shell.sources[index];\n\treturn source !== undefined && !source.escaped && source.quoteStart === undefined;\n}\n\nfunction bracketEnd(shell: NormalizedStaticShell, start: number, end: number): number | undefined {\n\tif (shell.text[start] !== \"[\" || !activeSource(shell, start)) return undefined;\n\tlet cursor = start + 1;\n\tif (cursor < end && (shell.text[cursor] === \"!\" || shell.text[cursor] === \"^\") && activeSource(shell, cursor)) {\n\t\tcursor++;\n\t}\n\tlet members = 0;\n\tif (cursor < end && shell.text[cursor] === \"]\" && activeSource(shell, cursor)) {\n\t\tmembers++;\n\t\tcursor++;\n\t}\n\twhile (cursor < end) {\n\t\tif (shell.text[cursor] === \"]\" && activeSource(shell, cursor) && members > 0) return cursor;\n\t\tif (\n\t\t\tshell.text[cursor] === \"[\" &&\n\t\t\tactiveSource(shell, cursor) &&\n\t\t\t\".=:\".includes(shell.text[cursor + 1] ?? \"\") &&\n\t\t\tactiveSource(shell, cursor + 1)\n\t\t) {\n\t\t\tconst terminator = `${shell.text[cursor + 1]}]`;\n\t\t\tconst nestedEnd = shell.text.indexOf(terminator, cursor + 2);\n\t\t\tif (nestedEnd >= 0 && nestedEnd + 1 < end && activeSource(shell, nestedEnd + 1)) {\n\t\t\t\tmembers++;\n\t\t\t\tcursor = nestedEnd + 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tmembers++;\n\t\tcursor++;\n\t}\n\treturn undefined;\n}\n\nfunction activeGlob(shell: NormalizedStaticShell, start: number, end: number): boolean {\n\tfor (let index = start; index < end; index++) {\n\t\tif (!activeSource(shell, index)) continue;\n\t\tconst character = shell.text[index];\n\t\tif (character === \"*\" || character === \"?\") return true;\n\t\tconst close = bracketEnd(shell, index, end);\n\t\tif (close !== undefined) return true;\n\t}\n\treturn false;\n}\n\nfunction separator(shell: NormalizedStaticShell, start: number, end: number, delimiters: string): number {\n\tfor (let index = start; index < end; index++) {\n\t\tconst close = bracketEnd(shell, index, end);\n\t\tif (close !== undefined) {\n\t\t\tindex = close;\n\t\t\tcontinue;\n\t\t}\n\t\tif (delimiters.includes(shell.text[index])) return index;\n\t}\n\treturn end;\n}\n\nfunction previousArgument(shell: NormalizedStaticShell, index: number): string | undefined {\n\tconst current = shell.words[index];\n\tconst previous = shell.words[index - 1];\n\treturn current !== undefined && previous !== undefined && /^\\s*$/.test(shell.text.slice(previous.end, current.start))\n\t\t? shell.text.slice(previous.start, previous.end)\n\t\t: undefined;\n}\n\nfunction nameLike(shell: NormalizedStaticShell, start: number, end: number, allowLeadingQuestion: boolean): boolean {\n\tconst first = shell.text[start];\n\tif (\n\t\tfirst === undefined ||\n\t\t(!/[A-Za-z_*_]/.test(first) && first !== \"[\" && !(allowLeadingQuestion && first === \"?\"))\n\t) {\n\t\treturn false;\n\t}\n\tfor (let index = start; index < end; index++) {\n\t\tconst close = bracketEnd(shell, index, end);\n\t\tif (close !== undefined) {\n\t\t\tindex = close;\n\t\t\tcontinue;\n\t\t}\n\t\tif (\"/:.\".includes(shell.text[index])) return false;\n\t}\n\treturn true;\n}\n\nfunction hasActiveQueryNameGlob(\n\tshell: NormalizedStaticShell,\n\twordStart: number,\n\twordEnd: number,\n\tcommandTreatsArgumentsAsUrls: boolean,\n): boolean {\n\tconst text = shell.text.slice(wordStart, wordEnd);\n\tconst question = text.indexOf(\"?\");\n\tif (question < 0) return false;\n\tconst markerStart = wordStart + question;\n\tconst hasQuerySeparator = separator(shell, markerStart + 1, wordEnd, \"=&#\") < wordEnd;\n\tconst structuralQuery = hasQuerySeparator && (question === 0 || text.slice(0, question).startsWith(\"/\"));\n\tif (!commandTreatsArgumentsAsUrls && !text.slice(0, question).includes(\"://\") && !structuralQuery) return false;\n\tlet marker = markerStart;\n\twhile (marker < wordEnd) {\n\t\tconst nameStart = marker + 1;\n\t\tconst nameEnd = separator(shell, nameStart, wordEnd, \"=&#\");\n\t\tif (activeGlob(shell, nameStart, nameEnd)) return true;\n\t\tif (nameEnd >= wordEnd || shell.text[nameEnd] === \"#\") return false;\n\t\tif (shell.text[nameEnd] === \"&\") {\n\t\t\tmarker = nameEnd;\n\t\t\tcontinue;\n\t\t}\n\t\tconst next = separator(shell, nameEnd + 1, wordEnd, \"&#\");\n\t\tif (next >= wordEnd || shell.text[next] === \"#\") return false;\n\t\tmarker = next;\n\t}\n\treturn false;\n}\n\nexport function hasActiveShellNameGlob(shell: NormalizedStaticShell): boolean {\n\tlet optionsEnded = false;\n\tlet commandName: string | undefined;\n\tlet commandWord = -1;\n\tlet wrapper: ShellCommandWrapperState | undefined;\n\tfor (let index = 0; index < shell.words.length; index++) {\n\t\tconst word = shell.words[index];\n\t\tconst previousWord = shell.words[index - 1];\n\t\tif (previousWord !== undefined && /[;&|()\\n]/.test(shell.text.slice(previousWord.end, word.start))) {\n\t\t\tif (wrapper !== undefined) return true;\n\t\t\toptionsEnded = false;\n\t\t\tcommandName = undefined;\n\t\t\twrapper = undefined;\n\t\t}\n\t\tconst text = shell.text.slice(word.start, word.end);\n\t\tconst previous = previousArgument(shell, index);\n\t\tconst assignment = separator(shell, word.start, word.end, \"=\");\n\t\tconst commandWasUnset = commandName === undefined;\n\t\tconst staticAssignment =\n\t\t\tassignment < word.end &&\n\t\t\tnameLike(shell, word.start, assignment, false) &&\n\t\t\t!activeGlob(shell, word.start, assignment);\n\t\tif (commandWasUnset && !staticAssignment) {\n\t\t\tcommandName = text;\n\t\t\tcommandWord = index;\n\t\t\twrapper = shellCommandWrapper(text);\n\t\t}\n\t\tlet wrapperOptionEnd: number | undefined;\n\t\tlet wrapperValue = false;\n\t\tif (wrapper !== undefined && index !== commandWord) {\n\t\t\tconst step = consumeShellCommandWrapper(wrapper, text);\n\t\t\tif (step.ambiguous) return true;\n\t\t\twrapper = step.state;\n\t\t\twrapperOptionEnd = step.optionEnd;\n\t\t\twrapperValue = step.value === true;\n\t\t\tif (step.target) {\n\t\t\t\tcommandName = text;\n\t\t\t\tcommandWord = index;\n\t\t\t\toptionsEnded = false;\n\t\t\t\twrapper = shellCommandWrapper(text);\n\t\t\t}\n\t\t}\n\t\tlet envCommand = commandName !== undefined && /(?:^|\\/)env(?:\\.exe)?$/.test(commandName);\n\t\tconst envAssignment = assignment < word.end && nameLike(shell, word.start, assignment, true);\n\t\tconst envOptionValue = envCommand && previous !== undefined && ENV_VALUE_OPTIONS.has(previous);\n\t\tif (envCommand && index !== commandWord && !text.startsWith(\"-\") && !envAssignment && !envOptionValue) {\n\t\t\tcommandName = text;\n\t\t\tcommandWord = index;\n\t\t\toptionsEnded = false;\n\t\t\twrapper = shellCommandWrapper(text);\n\t\t\tenvCommand = false;\n\t\t}\n\t\tconst curlCommand = commandName !== undefined && /(?:^|\\/)curl(?:\\.exe)?$/.test(commandName);\n\t\tconst currentCurlValue = !optionsEnded && curlCommand ? curlValue(text) : undefined;\n\t\tconst previousCurlValue =\n\t\t\t!optionsEnded && curlCommand && previous !== undefined ? curlValue(previous) : undefined;\n\t\tconst valueFromPrevious = previousCurlValue !== undefined && previousCurlValue.offset === previous?.length;\n\t\tconst fileValue =\n\t\t\t(currentCurlValue?.kind === \"file\" && currentCurlValue.offset < text.length) ||\n\t\t\t(valueFromPrevious && previousCurlValue?.kind === \"file\");\n\t\tconst headerValue =\n\t\t\t(currentCurlValue?.kind === \"header\" && currentCurlValue.offset < text.length) ||\n\t\t\t(valueFromPrevious && previousCurlValue?.kind === \"header\");\n\t\tconst optionValue = fileValue || headerValue || envOptionValue || wrapperValue;\n\t\tif (!optionsEnded && !optionValue && text === \"--\") {\n\t\t\toptionsEnded = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst optionPrefix =\n\t\t\t!optionsEnded && !valueFromPrevious && !envOptionValue && !wrapperValue\n\t\t\t\t? /^--?/.exec(text)?.[0].length\n\t\t\t\t: undefined;\n\t\tif (optionPrefix !== undefined) {\n\t\t\tconst optionEnd =\n\t\t\t\twrapperOptionEnd ??\n\t\t\t\tcurrentCurlValue?.offset ??\n\t\t\t\tseparator(shell, word.start + optionPrefix, word.end, \"=\") - word.start;\n\t\t\tif (activeGlob(shell, word.start + optionPrefix, word.start + optionEnd)) return true;\n\t\t}\n\t\tconst shellCommand = commandName !== undefined && /(?:^|\\/)(?:ba|da|k|z)?sh(?:\\.exe)?$/i.test(commandName);\n\t\tif (shellCommand && index !== commandWord && /^-[A-Za-z]*c[A-Za-z]*$/.test(text)) {\n\t\t\tconst script = previousArgument(shell, index + 1) === text ? shell.words[index + 1] : undefined;\n\t\t\tif (script !== undefined) normalizeStaticShell(shell.text.slice(script.start, script.end));\n\t\t}\n\t\tif (commandName !== undefined && /(?:^|\\/)eval$/.test(commandName) && index === commandWord + 1) {\n\t\t\tlet last = index;\n\t\t\twhile (\n\t\t\t\tpreviousArgument(shell, last + 1) === shell.text.slice(shell.words[last].start, shell.words[last].end)\n\t\t\t) {\n\t\t\t\tlast++;\n\t\t\t}\n\t\t\tnormalizeStaticShell(shell.text.slice(word.start, shell.words[last].end));\n\t\t}\n\t\tif (\n\t\t\t!fileValue &&\n\t\t\t!headerValue &&\n\t\t\thasActiveQueryNameGlob(shell, word.start, word.end, index !== commandWord && curlCommand)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tconst explicitEnvironment = index !== commandWord && (envCommand || commandName === \"export\");\n\t\tif (\n\t\t\t(commandWasUnset || explicitEnvironment) &&\n\t\t\tassignment < word.end &&\n\t\t\t(!text.startsWith(\"?\") || explicitEnvironment) &&\n\t\t\tnameLike(shell, word.start, assignment, explicitEnvironment) &&\n\t\t\tactiveGlob(shell, word.start, assignment)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tconst headerStart = headerValue\n\t\t\t? currentCurlValue?.kind === \"header\"\n\t\t\t\t? word.start + currentCurlValue.offset\n\t\t\t\t: word.start\n\t\t\t: undefined;\n\t\tif (headerStart !== undefined) {\n\t\t\tconst headerEnd = separator(shell, headerStart, word.end, \":=\");\n\t\t\tif (activeGlob(shell, headerStart, headerEnd)) return true;\n\t\t}\n\t}\n\treturn wrapper !== undefined;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export type CurlValue = {
2
+ readonly kind: "file" | "header";
3
+ readonly offset: number;
4
+ };
5
+ export declare const ENV_VALUE_OPTIONS: Set<string>;
6
+ export declare function curlValue(text: string): CurlValue | undefined;
7
+ //# sourceMappingURL=shell-command-option.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-command-option.d.ts","sourceRoot":"","sources":["../../src/guardrails/shell-command-option.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAGtF,eAAO,MAAM,iBAAiB,aAAuF,CAAC;AAEtH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAa7D","sourcesContent":["export type CurlValue = { readonly kind: \"file\" | \"header\"; readonly offset: number };\n\nconst CURL_FILE_LONG = [\"--config\", \"--output\", \"--upload-file\"] as const;\nexport const ENV_VALUE_OPTIONS = new Set([\"-C\", \"--chdir\", \"-S\", \"--split-string\", \"-a\", \"--argv0\", \"-u\", \"--unset\"]);\n\nexport function curlValue(text: string): CurlValue | undefined {\n\tfor (const option of CURL_FILE_LONG) {\n\t\tif (text === option) return { kind: \"file\", offset: text.length };\n\t\tif (text.startsWith(`${option}=`)) return { kind: \"file\", offset: option.length + 1 };\n\t}\n\tif (text === \"--header\") return { kind: \"header\", offset: text.length };\n\tif (text.startsWith(\"--header=\")) return { kind: \"header\", offset: 9 };\n\tif (!text.startsWith(\"-\") || text.startsWith(\"--\")) return undefined;\n\tfor (let index = 1; index < text.length; index++) {\n\t\tif (\"KoT\".includes(text[index])) return { kind: \"file\", offset: index + 1 };\n\t\tif (text[index] === \"H\") return { kind: \"header\", offset: index + 1 };\n\t}\n\treturn undefined;\n}\n"]}
@@ -0,0 +1,24 @@
1
+ const CURL_FILE_LONG = ["--config", "--output", "--upload-file"];
2
+ export const ENV_VALUE_OPTIONS = new Set(["-C", "--chdir", "-S", "--split-string", "-a", "--argv0", "-u", "--unset"]);
3
+ export function curlValue(text) {
4
+ for (const option of CURL_FILE_LONG) {
5
+ if (text === option)
6
+ return { kind: "file", offset: text.length };
7
+ if (text.startsWith(`${option}=`))
8
+ return { kind: "file", offset: option.length + 1 };
9
+ }
10
+ if (text === "--header")
11
+ return { kind: "header", offset: text.length };
12
+ if (text.startsWith("--header="))
13
+ return { kind: "header", offset: 9 };
14
+ if (!text.startsWith("-") || text.startsWith("--"))
15
+ return undefined;
16
+ for (let index = 1; index < text.length; index++) {
17
+ if ("KoT".includes(text[index]))
18
+ return { kind: "file", offset: index + 1 };
19
+ if (text[index] === "H")
20
+ return { kind: "header", offset: index + 1 };
21
+ }
22
+ return undefined;
23
+ }
24
+ //# sourceMappingURL=shell-command-option.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-command-option.js","sourceRoot":"","sources":["../../src/guardrails/shell-command-option.ts"],"names":[],"mappings":"AAEA,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,eAAe,CAAU,CAAC;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;AAEtH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAyB;IAC9D,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QACrC,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QAClE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;IACvF,CAAC;IACD,IAAI,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACxE,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACrE,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAClD,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC;QAC5E,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG;YAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC;IACvE,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB","sourcesContent":["export type CurlValue = { readonly kind: \"file\" | \"header\"; readonly offset: number };\n\nconst CURL_FILE_LONG = [\"--config\", \"--output\", \"--upload-file\"] as const;\nexport const ENV_VALUE_OPTIONS = new Set([\"-C\", \"--chdir\", \"-S\", \"--split-string\", \"-a\", \"--argv0\", \"-u\", \"--unset\"]);\n\nexport function curlValue(text: string): CurlValue | undefined {\n\tfor (const option of CURL_FILE_LONG) {\n\t\tif (text === option) return { kind: \"file\", offset: text.length };\n\t\tif (text.startsWith(`${option}=`)) return { kind: \"file\", offset: option.length + 1 };\n\t}\n\tif (text === \"--header\") return { kind: \"header\", offset: text.length };\n\tif (text.startsWith(\"--header=\")) return { kind: \"header\", offset: 9 };\n\tif (!text.startsWith(\"-\") || text.startsWith(\"--\")) return undefined;\n\tfor (let index = 1; index < text.length; index++) {\n\t\tif (\"KoT\".includes(text[index])) return { kind: \"file\", offset: index + 1 };\n\t\tif (text[index] === \"H\") return { kind: \"header\", offset: index + 1 };\n\t}\n\treturn undefined;\n}\n"]}
@@ -0,0 +1,29 @@
1
+ export interface ShellSourceRange {
2
+ readonly start: number;
3
+ readonly end: number;
4
+ readonly contentEnd: number;
5
+ readonly quoteStart?: number;
6
+ readonly kind: "literal" | "variable";
7
+ readonly escaped: boolean;
8
+ }
9
+ export interface StaticShellWord {
10
+ readonly start: number;
11
+ readonly end: number;
12
+ }
13
+ export interface StaticShellVariable {
14
+ readonly start: number;
15
+ readonly end: number;
16
+ readonly sourceStart: number;
17
+ readonly sourceEnd: number;
18
+ }
19
+ export interface NormalizedStaticShell {
20
+ readonly text: string;
21
+ readonly sources: readonly ShellSourceRange[];
22
+ readonly words: readonly StaticShellWord[];
23
+ readonly variables: readonly StaticShellVariable[];
24
+ }
25
+ export declare class StaticShellSyntaxError extends Error {
26
+ constructor();
27
+ }
28
+ export declare function normalizeStaticShell(text: string): NormalizedStaticShell;
29
+ //# sourceMappingURL=shell-command-tokenizer.d.ts.map