@svenroth-ai/shipwright 0.24.7-next.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/dist/assets/{EmbeddedTerminal-D2BLGWdW.js → EmbeddedTerminal-D3wpPNZP.js} +20 -20
- package/client/dist/assets/{abnfDiagram-VRR7QNED-C-W_2Jd4.js → abnfDiagram-VRR7QNED-BkLIltB3.js} +1 -1
- package/client/dist/assets/{arc-CpqwESP5.js → arc-Sw-Gh0nI.js} +1 -1
- package/client/dist/assets/{architectureDiagram-ZJ3FMSHR-DZVZ895V.js → architectureDiagram-ZJ3FMSHR-rdxUi9Ks.js} +1 -1
- package/client/dist/assets/{blockDiagram-677ZJIJ3-CCCZiAA7.js → blockDiagram-677ZJIJ3-Ch_9dj8a.js} +1 -1
- package/client/dist/assets/{c4Diagram-LMCZKHZV-Bg1beZAE.js → c4Diagram-LMCZKHZV-CkFlO65x.js} +1 -1
- package/client/dist/assets/channel-BYPbLjRl.js +1 -0
- package/client/dist/assets/{chunk-2Q5K7J3B-CoaXSarh.js → chunk-2Q5K7J3B-CypX83xu.js} +1 -1
- package/client/dist/assets/{chunk-32BRIVSS-CeFXS-CM.js → chunk-32BRIVSS-DxVLYGdl.js} +1 -1
- package/client/dist/assets/{chunk-5VM5RSS4-QyJMVe0m.js → chunk-5VM5RSS4-Cxk1evhx.js} +1 -1
- package/client/dist/assets/{chunk-EX3LRPZG-BIOtWVF8.js → chunk-EX3LRPZG-n9q5S4yJ.js} +1 -1
- package/client/dist/assets/{chunk-JWPE2WC7-Dcq-lWyX.js → chunk-JWPE2WC7-jeL4xvir.js} +1 -1
- package/client/dist/assets/{chunk-MOJQB5TN-C95323ja.js → chunk-MOJQB5TN-DDSHN0cj.js} +1 -1
- package/client/dist/assets/{chunk-RYQCIY6F-C0HPwpWy.js → chunk-RYQCIY6F-C0bo383g.js} +1 -1
- package/client/dist/assets/{chunk-V7JOEXUC-BPrTbZqA.js → chunk-V7JOEXUC-C0jDXXAU.js} +1 -1
- package/client/dist/assets/{chunk-VR4S4FIN--BCnNoqt.js → chunk-VR4S4FIN-h-Rr4Pn2.js} +1 -1
- package/client/dist/assets/{chunk-XXDRQBXY-Dj8vtcXf.js → chunk-XXDRQBXY-BexSo0-s.js} +1 -1
- package/client/dist/assets/classDiagram-OUVF2IWQ-CsFIeTju.js +1 -0
- package/client/dist/assets/classDiagram-v2-EOCWNBFH-CsFIeTju.js +1 -0
- package/client/dist/assets/{cose-bilkent-JH36ORCC-y51otZKz.js → cose-bilkent-JH36ORCC-BQXzaioj.js} +1 -1
- package/client/dist/assets/{cynefin-VYW2F7L2-BRfS3cHo.js → cynefin-VYW2F7L2-BJIofhfe.js} +1 -1
- package/client/dist/assets/{cynefinDiagram-TSTJHNR4-Dpg2RtfL.js → cynefinDiagram-TSTJHNR4-DmPqTM1T.js} +1 -1
- package/client/dist/assets/{dagre-VKFMJZFB-BdZtXgKZ.js → dagre-VKFMJZFB-1xljvw6X.js} +1 -1
- package/client/dist/assets/{diagram-FQU43EPY-K-RCwWxW.js → diagram-FQU43EPY-iyL9muCG.js} +1 -1
- package/client/dist/assets/{diagram-G47NLZAW-CHNn7F2-.js → diagram-G47NLZAW-DREp2Th6.js} +1 -1
- package/client/dist/assets/{diagram-NH7WQ7WH-B3zu6xib.js → diagram-NH7WQ7WH-h0X3Is2S.js} +1 -1
- package/client/dist/assets/{diagram-OA4YK3LP-Wp5ju7L7.js → diagram-OA4YK3LP-Bj8YZRFJ.js} +1 -1
- package/client/dist/assets/{diagram-WEI45ONY-C94GpsRk.js → diagram-WEI45ONY-BZOf805d.js} +1 -1
- package/client/dist/assets/{ebnfDiagram-CCIWWBDH-DXrTPOrP.js → ebnfDiagram-CCIWWBDH-DO5JnXda.js} +1 -1
- package/client/dist/assets/{erDiagram-Q63AITRT-CGejnW0f.js → erDiagram-Q63AITRT-DRgcqg1T.js} +1 -1
- package/client/dist/assets/{flowDiagram-23GEKE2U-CFJMM637.js → flowDiagram-23GEKE2U-C3J9QmrW.js} +1 -1
- package/client/dist/assets/{ganttDiagram-NO4QXBWP-DnaH94Su.js → ganttDiagram-NO4QXBWP-BIEl3Tkm.js} +1 -1
- package/client/dist/assets/{gitGraphDiagram-IHSO6WYX-DT-D0gbn.js → gitGraphDiagram-IHSO6WYX-BNF0tFWC.js} +1 -1
- package/client/dist/assets/index-BnTOrFHb.js +829 -0
- package/client/dist/assets/index-CYPc6qxw.css +1 -0
- package/client/dist/assets/{infoDiagram-FWYZ7A6U-DW3Ps00w.js → infoDiagram-FWYZ7A6U-DEyFxiyY.js} +1 -1
- package/client/dist/assets/{ishikawaDiagram-FXEZZL3T-DjxjIT3n.js → ishikawaDiagram-FXEZZL3T-B_ehlg3I.js} +1 -1
- package/client/dist/assets/{journeyDiagram-5HDEW3XC-DTQRI93Y.js → journeyDiagram-5HDEW3XC-Dp00FyJS.js} +1 -1
- package/client/dist/assets/{kanban-definition-HUTT4EX6-BeDvbiEs.js → kanban-definition-HUTT4EX6-CTAf16q8.js} +1 -1
- package/client/dist/assets/{linear-B4foxDlh.js → linear-9vNUgmYd.js} +1 -1
- package/client/dist/assets/{mermaid.core-c-L4qgSB.js → mermaid.core-A6tjaEVD.js} +4 -4
- package/client/dist/assets/{mindmap-definition-LN4V7U3C-dh8v0J8T.js → mindmap-definition-LN4V7U3C-DRW3bmCY.js} +1 -1
- package/client/dist/assets/{pegDiagram-2B236MQR-D5vNrtFh.js → pegDiagram-2B236MQR-DJFCjHp2.js} +1 -1
- package/client/dist/assets/{pieDiagram-ENE6RG2P-CIjg_6lR.js → pieDiagram-ENE6RG2P-KRd-sGvC.js} +1 -1
- package/client/dist/assets/{quadrantDiagram-ABIIQ3AL-CF37qWQD.js → quadrantDiagram-ABIIQ3AL-9TLZE3Hu.js} +1 -1
- package/client/dist/assets/{railroadDiagram-RFXS5EU6-x0Hn30Kj.js → railroadDiagram-RFXS5EU6-y4tLtjXg.js} +1 -1
- package/client/dist/assets/{requirementDiagram-TGXJPOKE-BE2QnVbN.js → requirementDiagram-TGXJPOKE-B9EOQDQM.js} +1 -1
- package/client/dist/assets/{sankeyDiagram-HTMAVEWB-CUv6cIlV.js → sankeyDiagram-HTMAVEWB-zfwiDzkT.js} +1 -1
- package/client/dist/assets/{sequenceDiagram-DBY2YBRQ-C3ACj5LS.js → sequenceDiagram-DBY2YBRQ-DW1LWepL.js} +1 -1
- package/client/dist/assets/{sizeCapture-X5ZJPWSS-DTMIb5gX.js → sizeCapture-X5ZJPWSS-BM81Mik0.js} +1 -1
- package/client/dist/assets/{stateDiagram-2N3HPSRC-DI1Z8P74.js → stateDiagram-2N3HPSRC-D0EMIw1w.js} +1 -1
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-B3E4_xxT.js +1 -0
- package/client/dist/assets/{swimlanes-5IMT3BWC-CLSr9w29.js → swimlanes-5IMT3BWC-CKXNCX6M.js} +2 -2
- package/client/dist/assets/swimlanesDiagram-G3AALYLV-BegQBqnC.js +8 -0
- package/client/dist/assets/{timeline-definition-FHXFAJF6-BTWmUhDh.js → timeline-definition-FHXFAJF6-ByzIfvL5.js} +1 -1
- package/client/dist/assets/{vennDiagram-L72KCM5P-Ce1dlLCM.js → vennDiagram-L72KCM5P-Xep38xBH.js} +1 -1
- package/client/dist/assets/{wardleyDiagram-EHGQE667-ClOE-aae.js → wardleyDiagram-EHGQE667-DKWDpx6g.js} +1 -1
- package/client/dist/assets/{xychartDiagram-FW5EYKEG-Fa2pqWKw.js → xychartDiagram-FW5EYKEG-BGcwZTnu.js} +1 -1
- package/client/dist/index.html +27 -27
- package/lib/claude-cli.mjs +173 -173
- package/lib/preflight.mjs +252 -252
- package/lib/probe-path.mjs +84 -84
- package/lib/server.mjs +369 -369
- package/lib/util.mjs +157 -157
- package/lib/win32-spawn.mjs +253 -253
- package/package.json +1 -1
- package/scripts/deploy-procs.mjs +208 -208
- package/scripts/deploy-swap.mjs +276 -276
- package/scripts/kill-targets.js +140 -140
- package/scripts/repair-claude-json.mjs +295 -295
- package/server/dist/config/default-actions.json +90 -90
- package/server/dist/core/grade-runner.d.ts +4 -4
- package/server/dist/core/grade-runner.d.ts.map +1 -1
- package/server/dist/core/grade-runner.js +51 -26
- package/server/dist/core/grade-runner.js.map +1 -1
- package/server/dist/core/grade-target.d.ts +8 -0
- package/server/dist/core/grade-target.d.ts.map +1 -1
- package/server/dist/core/grade-target.js +10 -0
- package/server/dist/core/grade-target.js.map +1 -1
- package/server/dist/core/mission-context/association.d.ts +46 -0
- package/server/dist/core/mission-context/association.d.ts.map +1 -1
- package/server/dist/core/mission-context/association.js +69 -0
- package/server/dist/core/mission-context/association.js.map +1 -1
- package/server/dist/core/mission-context/resolver-iterate.d.ts +6 -0
- package/server/dist/core/mission-context/resolver-iterate.d.ts.map +1 -0
- package/server/dist/core/mission-context/resolver-iterate.js +202 -0
- package/server/dist/core/mission-context/resolver-iterate.js.map +1 -0
- package/server/dist/core/mission-context/resolver-parts.d.ts +20 -0
- package/server/dist/core/mission-context/resolver-parts.d.ts.map +1 -1
- package/server/dist/core/mission-context/resolver-parts.js +92 -0
- package/server/dist/core/mission-context/resolver-parts.js.map +1 -1
- package/server/dist/core/mission-context/resolver.d.ts +1 -1
- package/server/dist/core/mission-context/resolver.d.ts.map +1 -1
- package/server/dist/core/mission-context/resolver.js +17 -199
- package/server/dist/core/mission-context/resolver.js.map +1 -1
- package/server/dist/core/mission-context/run-id-recovery.d.ts +21 -5
- package/server/dist/core/mission-context/run-id-recovery.d.ts.map +1 -1
- package/server/dist/core/mission-context/run-id-recovery.js +32 -5
- package/server/dist/core/mission-context/run-id-recovery.js.map +1 -1
- package/server/dist/core/mission-context/scenario.d.ts +18 -11
- package/server/dist/core/mission-context/scenario.d.ts.map +1 -1
- package/server/dist/core/mission-context/scenario.js +40 -3
- package/server/dist/core/mission-context/scenario.js.map +1 -1
- package/server/dist/core/path-guard.d.ts +1 -1
- package/server/dist/core/path-guard.d.ts.map +1 -1
- package/server/dist/core/path-guard.js +9 -2
- package/server/dist/core/path-guard.js.map +1 -1
- package/server/dist/core/shipslog-docs-reader.d.ts +4 -0
- package/server/dist/core/shipslog-docs-reader.d.ts.map +1 -0
- package/server/dist/core/shipslog-docs-reader.js +181 -0
- package/server/dist/core/shipslog-docs-reader.js.map +1 -0
- package/server/dist/core/shipslog-docs-types.d.ts +15 -0
- package/server/dist/core/shipslog-docs-types.d.ts.map +1 -0
- package/server/dist/core/shipslog-docs-types.js +14 -0
- package/server/dist/core/shipslog-docs-types.js.map +1 -0
- package/server/dist/core/triage-cli-runner.d.ts.map +1 -1
- package/server/dist/core/triage-cli-runner.js +29 -10
- package/server/dist/core/triage-cli-runner.js.map +1 -1
- package/server/dist/core/uv-runner.d.ts +23 -0
- package/server/dist/core/uv-runner.d.ts.map +1 -0
- package/server/dist/core/uv-runner.js +39 -0
- package/server/dist/core/uv-runner.js.map +1 -0
- package/server/dist/external/design-review/serve.js +27 -27
- package/server/dist/external/file/exists-routes.d.ts +7 -0
- package/server/dist/external/file/exists-routes.d.ts.map +1 -0
- package/server/dist/external/file/exists-routes.js +91 -0
- package/server/dist/external/file/exists-routes.js.map +1 -0
- package/server/dist/external/mission-context/association-write.d.ts +22 -0
- package/server/dist/external/mission-context/association-write.d.ts.map +1 -0
- package/server/dist/external/mission-context/association-write.js +90 -0
- package/server/dist/external/mission-context/association-write.js.map +1 -0
- package/server/dist/external/mission-context/routes.d.ts.map +1 -1
- package/server/dist/external/mission-context/routes.js +18 -31
- package/server/dist/external/mission-context/routes.js.map +1 -1
- package/server/dist/external/org/audit-log.d.ts +27 -0
- package/server/dist/external/org/audit-log.d.ts.map +1 -0
- package/server/dist/external/org/audit-log.js +92 -0
- package/server/dist/external/org/audit-log.js.map +1 -0
- package/server/dist/external/org/beat-register.d.ts +15 -22
- package/server/dist/external/org/beat-register.d.ts.map +1 -1
- package/server/dist/external/org/beat-register.js +37 -32
- package/server/dist/external/org/beat-register.js.map +1 -1
- package/server/dist/external/org/file-read.d.ts +16 -0
- package/server/dist/external/org/file-read.d.ts.map +1 -1
- package/server/dist/external/org/file-read.js +86 -46
- package/server/dist/external/org/file-read.js.map +1 -1
- package/server/dist/external/org/file-write-core.d.ts +18 -0
- package/server/dist/external/org/file-write-core.d.ts.map +1 -0
- package/server/dist/external/org/file-write-core.js +97 -0
- package/server/dist/external/org/file-write-core.js.map +1 -0
- package/server/dist/external/org/file-write.d.ts +25 -0
- package/server/dist/external/org/file-write.d.ts.map +1 -1
- package/server/dist/external/org/file-write.js +82 -132
- package/server/dist/external/org/file-write.js.map +1 -1
- package/server/dist/external/org/last-run.d.ts +25 -16
- package/server/dist/external/org/last-run.d.ts.map +1 -1
- package/server/dist/external/org/last-run.js +78 -64
- package/server/dist/external/org/last-run.js.map +1 -1
- package/server/dist/external/org/lead-doc-read.d.ts +21 -0
- package/server/dist/external/org/lead-doc-read.d.ts.map +1 -0
- package/server/dist/external/org/lead-doc-read.js +84 -0
- package/server/dist/external/org/lead-doc-read.js.map +1 -0
- package/server/dist/external/org/mount.d.ts +12 -0
- package/server/dist/external/org/mount.d.ts.map +1 -0
- package/server/dist/external/org/mount.js +34 -0
- package/server/dist/external/org/mount.js.map +1 -0
- package/server/dist/external/org/org-chart-lookup.d.ts +13 -0
- package/server/dist/external/org/org-chart-lookup.d.ts.map +1 -1
- package/server/dist/external/org/org-chart-lookup.js +40 -5
- package/server/dist/external/org/org-chart-lookup.js.map +1 -1
- package/server/dist/external/org/org-chart.d.ts +21 -12
- package/server/dist/external/org/org-chart.d.ts.map +1 -1
- package/server/dist/external/org/org-chart.js +50 -32
- package/server/dist/external/org/org-chart.js.map +1 -1
- package/server/dist/external/org/role-extract.d.ts +8 -0
- package/server/dist/external/org/role-extract.d.ts.map +1 -0
- package/server/dist/external/org/role-extract.js +72 -0
- package/server/dist/external/org/role-extract.js.map +1 -0
- package/server/dist/external/org/usage.d.ts +15 -11
- package/server/dist/external/org/usage.d.ts.map +1 -1
- package/server/dist/external/org/usage.js +64 -54
- package/server/dist/external/org/usage.js.map +1 -1
- package/server/dist/external/routes.d.ts +1 -1
- package/server/dist/external/routes.d.ts.map +1 -1
- package/server/dist/external/routes.js +19 -17
- package/server/dist/external/routes.js.map +1 -1
- package/server/dist/external/shipslog-docs/routes.d.ts +10 -0
- package/server/dist/external/shipslog-docs/routes.d.ts.map +1 -0
- package/server/dist/external/shipslog-docs/routes.js +41 -0
- package/server/dist/external/shipslog-docs/routes.js.map +1 -0
- package/server/dist/routes/grade.d.ts.map +1 -1
- package/server/dist/routes/grade.js +3 -2
- package/server/dist/routes/grade.js.map +1 -1
- package/server/dist/routes/org-leads-composite.d.ts +21 -0
- package/server/dist/routes/org-leads-composite.d.ts.map +1 -0
- package/server/dist/routes/org-leads-composite.js +95 -0
- package/server/dist/routes/org-leads-composite.js.map +1 -0
- package/server/dist/routes/org.d.ts +15 -0
- package/server/dist/routes/org.d.ts.map +1 -0
- package/server/dist/routes/org.js +198 -0
- package/server/dist/routes/org.js.map +1 -0
- package/server/dist/terminal/pty-manager.d.ts +9 -0
- package/server/dist/terminal/pty-manager.d.ts.map +1 -1
- package/server/dist/terminal/pty-manager.js +15 -0
- package/server/dist/terminal/pty-manager.js.map +1 -1
- package/server/dist/terminal/replay-snapshot.d.ts +10 -1
- package/server/dist/terminal/replay-snapshot.d.ts.map +1 -1
- package/server/dist/terminal/replay-snapshot.js +36 -1
- package/server/dist/terminal/replay-snapshot.js.map +1 -1
- package/server/dist/terminal/ws-upgrade-handler.d.ts +17 -1
- package/server/dist/terminal/ws-upgrade-handler.d.ts.map +1 -1
- package/server/dist/terminal/ws-upgrade-handler.fixtures.d.ts +1 -0
- package/server/dist/terminal/ws-upgrade-handler.fixtures.d.ts.map +1 -1
- package/server/dist/terminal/ws-upgrade-handler.fixtures.js +3 -2
- package/server/dist/terminal/ws-upgrade-handler.fixtures.js.map +1 -1
- package/server/dist/terminal/ws-upgrade-handler.js +45 -4
- package/server/dist/terminal/ws-upgrade-handler.js.map +1 -1
- package/server/dist/test/python-spawn-guard-util.d.ts +14 -0
- package/server/dist/test/python-spawn-guard-util.d.ts.map +1 -0
- package/server/dist/test/python-spawn-guard-util.js +124 -0
- package/server/dist/test/python-spawn-guard-util.js.map +1 -0
- package/server/dist/types/org.d.ts +115 -0
- package/server/dist/types/org.d.ts.map +1 -0
- package/server/dist/types/org.js +16 -0
- package/server/dist/types/org.js.map +1 -0
- package/server/package.json +1 -1
- package/server/profiles/PROFILE_SCHEMA_VERSION +1 -1
- package/server/profiles/README.md +27 -27
- package/server/profiles/python-plugin-monorepo.json +35 -35
- package/server/profiles/supabase-nextjs.json +253 -253
- package/server/profiles/vite-hono.json +92 -92
- package/client/dist/assets/MarkdownEditorModal-CP5yG9OU.js +0 -212
- package/client/dist/assets/channel-BNXbCvDS.js +0 -1
- package/client/dist/assets/classDiagram-OUVF2IWQ-w-wCtLx3.js +0 -1
- package/client/dist/assets/classDiagram-v2-EOCWNBFH-w-wCtLx3.js +0 -1
- package/client/dist/assets/index-CU2QJgvx.js +0 -609
- package/client/dist/assets/index-ftvVcUT8.css +0 -1
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-DvwiHlJv.js +0 -1
- package/client/dist/assets/swimlanesDiagram-G3AALYLV-JTQZTlZ3.js +0 -8
|
@@ -20,61 +20,101 @@ import { closeSync, constants as fsConstants, fstatSync, openSync, readFileSync
|
|
|
20
20
|
import { realPathGuard } from "../../core/path-guard.js";
|
|
21
21
|
import { resolveOrgAllowlistedTarget } from "./_helpers.js";
|
|
22
22
|
import { fileFingerprint } from "../file/_helpers.js";
|
|
23
|
-
|
|
23
|
+
/** Pure core — shared by the secret-gated route and the plain-surface proxy. */
|
|
24
|
+
export function orgFileReadCore(deps, relpath) {
|
|
24
25
|
const { leadsRoot } = deps;
|
|
25
26
|
const open = deps.openSync ?? openSync;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (!relpath || relpath.length === 0) {
|
|
28
|
+
return { status: 400, kind: "json", body: { error: "path_required" } };
|
|
29
|
+
}
|
|
30
|
+
const target = resolveOrgAllowlistedTarget(leadsRoot, relpath);
|
|
31
|
+
if (!target.ok) {
|
|
32
|
+
const status = target.reason === "not_allowlisted" ? 403 : 400;
|
|
33
|
+
const err = target.reason === "traversal" ? "path_traversal" : target.reason;
|
|
34
|
+
return { status, kind: "json", body: { error: err, detail: target.reason } };
|
|
35
|
+
}
|
|
36
|
+
// External-review fix (MEDIUM, spec): `realPathGuard` used to run BEFORE
|
|
37
|
+
// the open below. Its own docstring requires the target to already exist
|
|
38
|
+
// (it calls `realpathSync`, which throws ENOENT for a missing path) —
|
|
39
|
+
// calling it first meant a genuinely-missing allowlisted target (e.g. an
|
|
40
|
+
// as-yet-uncreated `conventions.md`) reported 400 `path_traversal`
|
|
41
|
+
// instead of 404 `not_found`, so the shared-documents viewer's not-found
|
|
42
|
+
// state (`OrgDocViewerModal`) was unreachable for any missing doc. Fixed
|
|
43
|
+
// by reordering to the same open-first pattern `lead-doc-read.ts` /
|
|
44
|
+
// `audit-log.ts` already use — see their comments for the rationale.
|
|
45
|
+
//
|
|
46
|
+
// CodeQL js/file-system-race fix: a separate lstat-check + stat + read
|
|
47
|
+
// (each its own syscall against the PATH) leaves a window where the
|
|
48
|
+
// final component could be swapped for a symlink between checks. Open
|
|
49
|
+
// ONCE with O_NOFOLLOW — the kernel atomically refuses a symlinked final
|
|
50
|
+
// component (ELOOP), so there is no gap left to race — then fstat/read
|
|
51
|
+
// the SAME fd, so what gets served is provably what got checked.
|
|
52
|
+
let fd;
|
|
53
|
+
try {
|
|
54
|
+
fd = open(target.absolute, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
const code = err?.code;
|
|
58
|
+
if (code === "ENOENT") {
|
|
59
|
+
return { status: 404, kind: "json", body: { error: "not_found", path: relpath } };
|
|
60
|
+
}
|
|
61
|
+
if (code === "ELOOP") {
|
|
62
|
+
return { status: 403, kind: "json", body: { error: "symlink_forbidden", path: relpath } };
|
|
30
63
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
64
|
+
return {
|
|
65
|
+
status: 500,
|
|
66
|
+
kind: "json",
|
|
67
|
+
body: { error: "file_stat_failed", detail: String(err).slice(0, 200) },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
if (!fstatSync(fd).isFile()) {
|
|
72
|
+
return { status: 400, kind: "json", body: { error: "not_a_file", path: relpath } };
|
|
36
73
|
}
|
|
74
|
+
// Existence is now confirmed (the open above succeeded) — realPathGuard's
|
|
75
|
+
// own contract requires exactly this ordering.
|
|
37
76
|
const realGuard = realPathGuard(leadsRoot, target.absolute);
|
|
38
77
|
if (!realGuard.ok) {
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// ONCE with O_NOFOLLOW — the kernel atomically refuses a symlinked final
|
|
45
|
-
// component (ELOOP), so there is no gap left to race — then fstat/read
|
|
46
|
-
// the SAME fd, so what gets served is provably what got checked.
|
|
47
|
-
let fd;
|
|
48
|
-
try {
|
|
49
|
-
fd = open(target.absolute, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
const code = err?.code;
|
|
53
|
-
if (code === "ENOENT") {
|
|
54
|
-
return c.json({ error: "not_found", path: relpath }, 404);
|
|
55
|
-
}
|
|
56
|
-
if (code === "ELOOP") {
|
|
57
|
-
return c.json({ error: "symlink_forbidden", path: relpath }, 403);
|
|
58
|
-
}
|
|
59
|
-
return c.json({ error: "file_stat_failed", detail: String(err).slice(0, 200) }, 500);
|
|
78
|
+
return {
|
|
79
|
+
status: 403,
|
|
80
|
+
kind: "json",
|
|
81
|
+
body: { error: "symlink_forbidden", detail: realGuard.reason, path: relpath },
|
|
82
|
+
};
|
|
60
83
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
const body = readFileSync(fd);
|
|
85
|
+
return {
|
|
86
|
+
status: 200,
|
|
87
|
+
kind: "file",
|
|
88
|
+
body,
|
|
89
|
+
headers: {
|
|
90
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
91
|
+
"X-Content-Type-Options": "nosniff",
|
|
92
|
+
ETag: `"${fileFingerprint(body)}"`,
|
|
93
|
+
"Cache-Control": "private, max-age=0, must-revalidate",
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
return {
|
|
99
|
+
status: 500,
|
|
100
|
+
kind: "json",
|
|
101
|
+
body: { error: "file_read_failed", detail: String(err).slice(0, 200) },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
closeSync(fd);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function registerOrgFileRead(app, deps) {
|
|
109
|
+
app.get("/api/external/org/file", async (c) => {
|
|
110
|
+
const result = orgFileReadCore(deps, c.req.query("path"));
|
|
111
|
+
if (result.kind === "json") {
|
|
112
|
+
return c.json(result.body, result.status);
|
|
74
113
|
}
|
|
75
|
-
|
|
76
|
-
|
|
114
|
+
for (const [key, value] of Object.entries(result.headers)) {
|
|
115
|
+
c.header(key, value);
|
|
77
116
|
}
|
|
117
|
+
return c.body(new Uint8Array(result.body), result.status);
|
|
78
118
|
});
|
|
79
119
|
}
|
|
80
120
|
//# sourceMappingURL=file-read.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-read.js","sourceRoot":"","sources":["../../../src/external/org/file-read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,SAAS,EAAE,SAAS,IAAI,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjG,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"file-read.js","sourceRoot":"","sources":["../../../src/external/org/file-read.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type WriteOutcome = {
|
|
2
|
+
ok: true;
|
|
3
|
+
status: 200;
|
|
4
|
+
body: {
|
|
5
|
+
written: true;
|
|
6
|
+
fingerprint: string;
|
|
7
|
+
size: number;
|
|
8
|
+
};
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
status: number;
|
|
12
|
+
body: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export declare function performWrite(absoluteTarget: string, leadsRoot: string, relpath: string, body: string, ifMatch: string | undefined, lstat: (path: string) => {
|
|
15
|
+
isSymbolicLink(): boolean;
|
|
16
|
+
isFile(): boolean;
|
|
17
|
+
}): WriteOutcome;
|
|
18
|
+
//# sourceMappingURL=file-write-core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-write-core.d.ts","sourceRoot":"","sources":["../../../src/external/org/file-write-core.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACrF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAEjE,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;IAAE,cAAc,IAAI,OAAO,CAAC;IAAC,MAAM,IAAI,OAAO,CAAA;CAAE,GACxE,YAAY,CAgFd"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/org/file-write-core.ts — the low-level atomic write-with-
|
|
3
|
+
* precondition primitive `file-write.ts`'s `orgFileWriteCore` calls.
|
|
4
|
+
*
|
|
5
|
+
* Split out purely to stay under the 300-line file guideline (CLAUDE.md) —
|
|
6
|
+
* mirrors the existing `beat-register-release.ts` /
|
|
7
|
+
* `beat-register-release-core.ts` split in this same directory. No new
|
|
8
|
+
* behavior; `performWrite`'s body and `WriteOutcome`'s shape are unchanged
|
|
9
|
+
* from before the split.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, writeFileSync, renameSync, unlinkSync, existsSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { realPathGuard } from "../../core/path-guard.js";
|
|
14
|
+
import { fileFingerprint, unquoteEtag } from "../file/_helpers.js";
|
|
15
|
+
export function performWrite(absoluteTarget, leadsRoot, relpath, body, ifMatch, lstat) {
|
|
16
|
+
let lst;
|
|
17
|
+
try {
|
|
18
|
+
lst = lstat(absoluteTarget);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const code = err?.code;
|
|
22
|
+
if (code === "ENOENT") {
|
|
23
|
+
return { ok: false, status: 404, body: { error: "not_found", path: relpath } };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
status: 500,
|
|
28
|
+
body: { error: "file_stat_failed", detail: String(err).slice(0, 200) },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (lst.isSymbolicLink()) {
|
|
32
|
+
return { ok: false, status: 403, body: { error: "symlink_forbidden", path: relpath } };
|
|
33
|
+
}
|
|
34
|
+
if (!lst.isFile()) {
|
|
35
|
+
return { ok: false, status: 400, body: { error: "not_a_file", path: relpath } };
|
|
36
|
+
}
|
|
37
|
+
const realGuard = realPathGuard(leadsRoot, absoluteTarget);
|
|
38
|
+
if (!realGuard.ok) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
status: 400,
|
|
42
|
+
body: { error: "path_traversal", detail: realGuard.reason },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (!ifMatch) {
|
|
46
|
+
return { ok: false, status: 400, body: { error: "precondition_required" } };
|
|
47
|
+
}
|
|
48
|
+
let current;
|
|
49
|
+
try {
|
|
50
|
+
current = readFileSync(absoluteTarget);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const code = err?.code;
|
|
54
|
+
if (code === "ENOENT") {
|
|
55
|
+
return { ok: false, status: 404, body: { error: "not_found", path: relpath } };
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
status: 500,
|
|
60
|
+
body: { error: "file_read_failed", detail: String(err).slice(0, 200) },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const currentFingerprint = fileFingerprint(current);
|
|
64
|
+
if (unquoteEtag(ifMatch) !== currentFingerprint) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
status: 409,
|
|
68
|
+
body: { error: "fingerprint_mismatch", currentFingerprint },
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const nextBuf = Buffer.from(body, "utf8");
|
|
72
|
+
const tmp = join(dirname(absoluteTarget), `.org-write.tmp-${process.pid}-${Date.now()}`);
|
|
73
|
+
try {
|
|
74
|
+
writeFileSync(tmp, nextBuf);
|
|
75
|
+
renameSync(tmp, absoluteTarget);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
try {
|
|
79
|
+
if (existsSync(tmp))
|
|
80
|
+
unlinkSync(tmp);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* swallow */
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
status: 500,
|
|
88
|
+
body: { error: "write_failed", detail: String(err).slice(0, 200), path: relpath },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
ok: true,
|
|
93
|
+
status: 200,
|
|
94
|
+
body: { written: true, fingerprint: fileFingerprint(nextBuf), size: nextBuf.length },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=file-write-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-write-core.js","sourceRoot":"","sources":["../../../src/external/org/file-write-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,5 +10,30 @@ export interface OrgFileWriteDeps extends DecisionsLockDeps {
|
|
|
10
10
|
/** Injectable for tests; production wires the real `withDecisionsLock`. */
|
|
11
11
|
withDecisionsLock?: typeof withDecisionsLock;
|
|
12
12
|
}
|
|
13
|
+
export type OrgFileWriteCoreResult = {
|
|
14
|
+
status: 200;
|
|
15
|
+
body: {
|
|
16
|
+
written: true;
|
|
17
|
+
fingerprint: string;
|
|
18
|
+
size: number;
|
|
19
|
+
};
|
|
20
|
+
} | {
|
|
21
|
+
status: 400 | 403 | 404 | 409 | 413 | 500;
|
|
22
|
+
body: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
export interface OrgFileWriteParams {
|
|
25
|
+
relpath: string | undefined;
|
|
26
|
+
contentLengthHeader: string | undefined;
|
|
27
|
+
body: string;
|
|
28
|
+
ifMatch: string | undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Pure core — shared by the secret-gated route and the plain-surface proxy.
|
|
32
|
+
* Kind restriction (e.g. "only `charter` may pass through the browser-facing
|
|
33
|
+
* proxy") is the CALLER's job, applied against `resolveOrgAllowlistedTarget`'s
|
|
34
|
+
* `kind` before or after invoking this — this core stays as general as the
|
|
35
|
+
* existing route (all six allowlisted kinds).
|
|
36
|
+
*/
|
|
37
|
+
export declare function orgFileWriteCore(deps: OrgFileWriteDeps, params: OrgFileWriteParams): Promise<OrgFileWriteCoreResult>;
|
|
13
38
|
export declare function registerOrgFileWrite(app: Hono, deps: OrgFileWriteDeps): void;
|
|
14
39
|
//# sourceMappingURL=file-write.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-write.d.ts","sourceRoot":"","sources":["../../../src/external/org/file-write.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"file-write.d.ts","sourceRoot":"","sources":["../../../src/external/org/file-write.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAOjC,OAAO,EACL,iBAAiB,EAEjB,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAG7B,sFAAsF;AACtF,eAAO,MAAM,mBAAmB,QAAkB,CAAC;AAOnD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,cAAc,IAAI,OAAO,CAAC;QAAC,MAAM,IAAI,OAAO,CAAA;KAAE,CAAC;IAC/E,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,OAAO,iBAAiB,CAAC;CAC9C;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC3E;IAAE,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,sBAAsB,CAAC,CA6EjC;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAyB5E"}
|
|
@@ -42,164 +42,114 @@
|
|
|
42
42
|
* 413 body over ORG_WRITE_MAX_BYTES
|
|
43
43
|
* 500 stat / read / write failure
|
|
44
44
|
*/
|
|
45
|
-
import {
|
|
46
|
-
import { dirname, join } from "node:path";
|
|
47
|
-
import { realPathGuard } from "../../core/path-guard.js";
|
|
45
|
+
import { lstatSync } from "node:fs";
|
|
48
46
|
import { resolveOrgAllowlistedTarget, } from "./_helpers.js";
|
|
49
|
-
import { fileFingerprint, unquoteEtag } from "../file/_helpers.js";
|
|
50
47
|
import { withDecisionsLock, OrgSymlinkEscapeError, } from "./decisions-lock.js";
|
|
48
|
+
import { performWrite } from "./file-write-core.js";
|
|
51
49
|
/** 2 MiB — org docs are small prose files, same cap as the project markdown write. */
|
|
52
50
|
export const ORG_WRITE_MAX_BYTES = 2 * 1024 * 1024;
|
|
53
51
|
const LOCKED_KINDS = new Set([
|
|
54
52
|
"decision_log",
|
|
55
53
|
"decisions_proposed",
|
|
56
54
|
]);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
if (lst.isSymbolicLink()) {
|
|
74
|
-
return { ok: false, status: 403, body: { error: "symlink_forbidden", path: relpath } };
|
|
75
|
-
}
|
|
76
|
-
if (!lst.isFile()) {
|
|
77
|
-
return { ok: false, status: 400, body: { error: "not_a_file", path: relpath } };
|
|
78
|
-
}
|
|
79
|
-
const realGuard = realPathGuard(leadsRoot, absoluteTarget);
|
|
80
|
-
if (!realGuard.ok) {
|
|
81
|
-
return {
|
|
82
|
-
ok: false,
|
|
83
|
-
status: 400,
|
|
84
|
-
body: { error: "path_traversal", detail: realGuard.reason },
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
if (!ifMatch) {
|
|
88
|
-
return { ok: false, status: 400, body: { error: "precondition_required" } };
|
|
55
|
+
/**
|
|
56
|
+
* Pure core — shared by the secret-gated route and the plain-surface proxy.
|
|
57
|
+
* Kind restriction (e.g. "only `charter` may pass through the browser-facing
|
|
58
|
+
* proxy") is the CALLER's job, applied against `resolveOrgAllowlistedTarget`'s
|
|
59
|
+
* `kind` before or after invoking this — this core stays as general as the
|
|
60
|
+
* existing route (all six allowlisted kinds).
|
|
61
|
+
*/
|
|
62
|
+
export async function orgFileWriteCore(deps, params) {
|
|
63
|
+
const { leadsRoot } = deps;
|
|
64
|
+
const lstat = deps.lstatSync ?? ((p) => lstatSync(p));
|
|
65
|
+
const lockFn = deps.withDecisionsLock ?? withDecisionsLock;
|
|
66
|
+
const { relpath, contentLengthHeader, body, ifMatch } = params;
|
|
67
|
+
if (!relpath || relpath.length === 0) {
|
|
68
|
+
return { status: 400, body: { error: "path_required" } };
|
|
89
69
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
70
|
+
const target = resolveOrgAllowlistedTarget(leadsRoot, relpath);
|
|
71
|
+
if (!target.ok) {
|
|
72
|
+
const status = target.reason === "not_allowlisted" ? 403 : 400;
|
|
73
|
+
const err = target.reason === "traversal" ? "path_traversal" : target.reason;
|
|
74
|
+
return { status, body: { error: err, detail: target.reason } };
|
|
93
75
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (code === "ENOENT") {
|
|
97
|
-
return { ok: false, status: 404, body: { error: "not_found", path: relpath } };
|
|
98
|
-
}
|
|
76
|
+
const declaredLength = Number(contentLengthHeader ?? "");
|
|
77
|
+
if (Number.isFinite(declaredLength) && declaredLength > ORG_WRITE_MAX_BYTES) {
|
|
99
78
|
return {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
body: { error: "file_read_failed", detail: String(err).slice(0, 200) },
|
|
79
|
+
status: 413,
|
|
80
|
+
body: { error: "payload_too_large", maxBytes: ORG_WRITE_MAX_BYTES, size: declaredLength },
|
|
103
81
|
};
|
|
104
82
|
}
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
83
|
+
const byteLen = Buffer.byteLength(body, "utf8");
|
|
84
|
+
if (byteLen > ORG_WRITE_MAX_BYTES) {
|
|
107
85
|
return {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
body: { error: "fingerprint_mismatch", currentFingerprint },
|
|
86
|
+
status: 413,
|
|
87
|
+
body: { error: "payload_too_large", maxBytes: ORG_WRITE_MAX_BYTES, size: byteLen },
|
|
111
88
|
};
|
|
112
89
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
catch (err) {
|
|
90
|
+
let outcome;
|
|
91
|
+
if (LOCKED_KINDS.has(target.kind)) {
|
|
92
|
+
// Pre-lock existence probe (external-review fix) — must run BEFORE
|
|
93
|
+
// withDecisionsLock, since its own ensureFile() would otherwise
|
|
94
|
+
// auto-vivify decisions-proposed.md first and hide a genuinely
|
|
95
|
+
// missing target from performWrite's existence check.
|
|
120
96
|
try {
|
|
121
|
-
|
|
122
|
-
unlinkSync(tmp);
|
|
97
|
+
lstat(target.absolute);
|
|
123
98
|
}
|
|
124
|
-
catch {
|
|
125
|
-
|
|
99
|
+
catch (err) {
|
|
100
|
+
const code = err?.code;
|
|
101
|
+
if (code === "ENOENT") {
|
|
102
|
+
return { status: 404, body: { error: "not_found", path: relpath } };
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
status: 500,
|
|
106
|
+
body: { error: "file_stat_failed", detail: String(err).slice(0, 200) },
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
outcome = await lockFn({ leadsRoot, lstatSync: deps.lstatSync }, () => performWrite(target.absolute, leadsRoot, relpath, body, ifMatch, lstat));
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
// Doubt-review fix: `withDecisionsLock` symlink-checks BOTH
|
|
114
|
+
// decision_log.md and decisions-proposed.md (whichever one this PUT
|
|
115
|
+
// targets, the OTHER is still checked because it shares the lock) —
|
|
116
|
+
// uncaught, that 403-shaped condition fell through to the generic
|
|
117
|
+
// 500 handler instead of the family's typed symlink_forbidden.
|
|
118
|
+
// ELOCKED is deliberately NOT caught here: it propagates to the
|
|
119
|
+
// caller (either the app-level errorHandler for the gated route, or
|
|
120
|
+
// the new router's own catch, both of which map it to the same
|
|
121
|
+
// retryable 409 every other multi-writer state file uses (CLAUDE.md
|
|
122
|
+
// DO-NOT #6)).
|
|
123
|
+
if (err instanceof OrgSymlinkEscapeError) {
|
|
124
|
+
return { status: 403, body: { error: "symlink_forbidden", path: err.path } };
|
|
125
|
+
}
|
|
126
|
+
throw err;
|
|
126
127
|
}
|
|
127
|
-
return {
|
|
128
|
-
ok: false,
|
|
129
|
-
status: 500,
|
|
130
|
-
body: { error: "write_failed", detail: String(err).slice(0, 200), path: relpath },
|
|
131
|
-
};
|
|
132
128
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
};
|
|
129
|
+
else {
|
|
130
|
+
outcome = performWrite(target.absolute, leadsRoot, relpath, body, ifMatch, lstat);
|
|
131
|
+
}
|
|
132
|
+
return { status: outcome.status, body: outcome.body };
|
|
138
133
|
}
|
|
139
134
|
export function registerOrgFileWrite(app, deps) {
|
|
140
|
-
const { leadsRoot } = deps;
|
|
141
|
-
const lstat = deps.lstatSync ?? ((p) => lstatSync(p));
|
|
142
|
-
const lockFn = deps.withDecisionsLock ?? withDecisionsLock;
|
|
143
135
|
app.put("/api/external/org/file", async (c) => {
|
|
144
136
|
const relpath = c.req.query("path");
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return c.json({ error: err, detail: target.reason }, status);
|
|
153
|
-
}
|
|
154
|
-
const declaredLength = Number(c.req.header("content-length") ?? "");
|
|
155
|
-
if (Number.isFinite(declaredLength) && declaredLength > ORG_WRITE_MAX_BYTES) {
|
|
137
|
+
const contentLengthHeader = c.req.header("content-length");
|
|
138
|
+
// Bail before reading the request body when the declared Content-Length
|
|
139
|
+
// alone already exceeds the cap — preserved from the pre-extraction
|
|
140
|
+
// handler so an oversized upload is never buffered into memory just to
|
|
141
|
+
// be rejected.
|
|
142
|
+
const declaredLength = Number(contentLengthHeader ?? "");
|
|
143
|
+
if (relpath && Number.isFinite(declaredLength) && declaredLength > ORG_WRITE_MAX_BYTES) {
|
|
156
144
|
return c.json({ error: "payload_too_large", maxBytes: ORG_WRITE_MAX_BYTES, size: declaredLength }, 413);
|
|
157
145
|
}
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (LOCKED_KINDS.has(target.kind)) {
|
|
166
|
-
// Pre-lock existence probe (external-review fix) — must run BEFORE
|
|
167
|
-
// withDecisionsLock, since its own ensureFile() would otherwise
|
|
168
|
-
// auto-vivify decisions-proposed.md first and hide a genuinely
|
|
169
|
-
// missing target from performWrite's existence check.
|
|
170
|
-
try {
|
|
171
|
-
lstat(target.absolute);
|
|
172
|
-
}
|
|
173
|
-
catch (err) {
|
|
174
|
-
const code = err?.code;
|
|
175
|
-
if (code === "ENOENT") {
|
|
176
|
-
return c.json({ error: "not_found", path: relpath }, 404);
|
|
177
|
-
}
|
|
178
|
-
return c.json({ error: "file_stat_failed", detail: String(err).slice(0, 200) }, 500);
|
|
179
|
-
}
|
|
180
|
-
try {
|
|
181
|
-
outcome = await lockFn({ leadsRoot, lstatSync: deps.lstatSync }, () => performWrite(target.absolute, leadsRoot, relpath, body, ifMatch, lstat));
|
|
182
|
-
}
|
|
183
|
-
catch (err) {
|
|
184
|
-
// Doubt-review fix: `withDecisionsLock` symlink-checks BOTH
|
|
185
|
-
// decision_log.md and decisions-proposed.md (whichever one this PUT
|
|
186
|
-
// targets, the OTHER is still checked because it shares the lock) —
|
|
187
|
-
// uncaught, that 403-shaped condition fell through to the generic
|
|
188
|
-
// 500 handler instead of the family's typed symlink_forbidden.
|
|
189
|
-
// ELOCKED is deliberately NOT caught here: it propagates to the
|
|
190
|
-
// app-level errorHandler (middleware/error-handler.ts), which already
|
|
191
|
-
// maps it to the same retryable 409 every other multi-writer state
|
|
192
|
-
// file uses (CLAUDE.md DO-NOT #6).
|
|
193
|
-
if (err instanceof OrgSymlinkEscapeError) {
|
|
194
|
-
return c.json({ error: "symlink_forbidden", path: err.path }, 403);
|
|
195
|
-
}
|
|
196
|
-
throw err;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
outcome = performWrite(target.absolute, leadsRoot, relpath, body, ifMatch, lstat);
|
|
201
|
-
}
|
|
202
|
-
return c.json(outcome.body, outcome.status);
|
|
146
|
+
const result = await orgFileWriteCore(deps, {
|
|
147
|
+
relpath,
|
|
148
|
+
contentLengthHeader,
|
|
149
|
+
body: await c.req.text(),
|
|
150
|
+
ifMatch: c.req.header("if-match"),
|
|
151
|
+
});
|
|
152
|
+
return c.json(result.body, result.status);
|
|
203
153
|
});
|
|
204
154
|
}
|
|
205
155
|
//# sourceMappingURL=file-write.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-write.js","sourceRoot":"","sources":["../../../src/external/org/file-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAGH,OAAO,
|
|
1
|
+
{"version":3,"file":"file-write.js","sourceRoot":"","sources":["../../../src/external/org/file-write.ts"],"names":[],"mappings":"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"}
|