sliccy 6.129.0 → 6.130.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +1 -1
  2. package/dist/node-server/_shared/secret-env-schema.d.ts +22 -0
  3. package/dist/node-server/_shared/secret-env-schema.d.ts.map +1 -1
  4. package/dist/node-server/_shared/secret-env-schema.js +28 -0
  5. package/dist/node-server/_shared/secret-env-schema.js.map +1 -1
  6. package/dist/node-server/routes/secrets.js +14 -1
  7. package/dist/node-server/secrets/env-secret-store.js +7 -1
  8. package/dist/ui/.vite/manifest.json +192 -179
  9. package/dist/ui/assets/CLAUDE-DhkULx_E.js +1 -0
  10. package/dist/ui/assets/__vite-browser-external-B19j3y2v.js +1 -0
  11. package/dist/ui/assets/{account-store-k2cwPcf2.js → account-store-Bt3zihHK.js} +1 -1
  12. package/dist/ui/assets/{account-store-BdEP74XJ.js → account-store-XKjChfD5.js} +1 -1
  13. package/dist/ui/assets/{adobe-fbWPKVjt.js → adobe-B7sd2ey1.js} +1 -1
  14. package/dist/ui/assets/{adobe-BwLPv0hM.js → adobe-DWv_nW7C.js} +1 -1
  15. package/dist/ui/assets/{agent-bridge-DfHxYJHJ.js → agent-bridge-CmEtyUJl.js} +1 -1
  16. package/dist/ui/assets/{apply-layout-BJ1rBekE.js → apply-layout-D-QW1Bgf.js} +1 -1
  17. package/dist/ui/assets/{apps-r9PcIbxT.js → apps-Dl5abdz0.js} +1 -1
  18. package/dist/ui/assets/{azure-openai-CgYOAd8p.js → azure-openai-CZHEfw9U.js} +1 -1
  19. package/dist/ui/assets/{azure-openai-DZhCYfxl.js → azure-openai-rq6LQC_C.js} +1 -1
  20. package/dist/ui/assets/{bsh-watchdog-B13hSKa_.js → bsh-watchdog-D97yX3sQ.js} +1 -1
  21. package/dist/ui/assets/{capability-Cs03kkRf.js → capability-Btlr4Wc-.js} +2 -2
  22. package/dist/ui/assets/{cerebras-CiQ7rFfE.js → cerebras-CjhIAx1j.js} +1 -1
  23. package/dist/ui/assets/{cerebras-h0n808h8.js → cerebras-UuG-yCJ_.js} +1 -1
  24. package/dist/ui/assets/{composer-speech-CDkeUAGH.js → composer-speech-C8OcijP-.js} +1 -1
  25. package/dist/ui/assets/{connect-surface-YLhncvhi.js → connect-surface-AitKSc2i.js} +1 -1
  26. package/dist/ui/assets/{core-xZ6s8sJH.js → core-CdPvxBo3.js} +1 -1
  27. package/dist/ui/assets/{dip-BLy1TG8X.js → dip-Co-Ga2Ny.js} +1 -1
  28. package/dist/ui/assets/{directed-approval-8JrfycF7.js → directed-approval-Ca0RuudO.js} +1 -1
  29. package/dist/ui/assets/{ensure-speech-assets-B1UCV_5w.js → ensure-speech-assets-Csum3_gw.js} +1 -1
  30. package/dist/ui/assets/{esbuild-wasm-BtINJtvc.js → esbuild-wasm-CSKOA492.js} +1 -1
  31. package/dist/ui/assets/{extension-media-capture-COQ32TYR.js → extension-media-capture-DiCJSgES.js} +1 -1
  32. package/dist/ui/assets/{fs-wEN7dsdI.js → fs-0AC_1kYN.js} +2 -2
  33. package/dist/ui/assets/{fs-BBl_N5G9.js → fs-ClRFZsKp.js} +1 -1
  34. package/dist/ui/assets/{github-DX02oPjV.js → github-Bo32za86.js} +1 -1
  35. package/dist/ui/assets/{github-BLiAAyB7.js → github-DA1dJZHt.js} +2 -2
  36. package/dist/ui/assets/{github-copilot-Bm-BQxfx.js → github-copilot-BlR9rGzE.js} +1 -1
  37. package/dist/ui/assets/{github-copilot-CbQvhVMh.js → github-copilot-hBCBR7n3.js} +1 -1
  38. package/dist/ui/assets/{github-install-lVUtA4h4.js → github-install-c-7WlKF0.js} +1 -1
  39. package/dist/ui/assets/{github-zip-CMgYq_Mz.js → github-zip-BAPGRyWK.js} +1 -1
  40. package/dist/ui/assets/{guest-tool-gate-AREwJZ9u.js → guest-tool-gate-CG9Yw8Kq.js} +1 -1
  41. package/dist/ui/assets/{hear-V35zSETD.js → hear-D7MO-jTh.js} +1 -1
  42. package/dist/ui/assets/{help-a0ozY9CT.js → help-LDG2J6R7.js} +1 -1
  43. package/dist/ui/assets/{installer-DtOMoVHi.js → installer-7BWtTgd6.js} +1 -1
  44. package/dist/ui/assets/{js-realm-worker-DA8yxQNR.js → js-realm-worker-CLGBN8Zo.js} +1 -1
  45. package/dist/ui/assets/{kernel-worker-CaWMG-jx.js → kernel-worker-V_qJr_00.js} +471 -471
  46. package/dist/ui/assets/{local-llm-2qwDfR7y.js → local-llm-Ko45nuy7.js} +1 -1
  47. package/dist/ui/assets/{local-llm-BkhpTkPI.js → local-llm-Z6hqEJOJ.js} +1 -1
  48. package/dist/ui/assets/{magick-wasm-74jUvNbY.js → magick-wasm-BEYfJbAX.js} +1 -1
  49. package/dist/ui/assets/{main-B6jyx1yC.js → main-DG-TOKaI.js} +3 -3
  50. package/dist/ui/assets/{mime-types-k2E6ulyv.js → mime-types-D0xZwlMO.js} +1 -1
  51. package/dist/ui/assets/{mount-DipGtzS7.js → mount-BrmPOs2_.js} +1 -1
  52. package/dist/ui/assets/{mount-C4oKyNyx.js → mount-O6L8YW8P.js} +3 -3
  53. package/dist/ui/assets/{mount-recovery-CWdxArDW.js → mount-recovery-lKw_Vyz_.js} +1 -1
  54. package/dist/ui/assets/{new-session-CqXRbPaf.js → new-session-Bl4ZsQ9b.js} +2 -2
  55. package/dist/ui/assets/{oauth-bootstrap-vTqMxvjN.js → oauth-bootstrap-Ch6w-P0O.js} +2 -2
  56. package/dist/ui/assets/{openai-codex-D-AgjjaY.js → openai-codex-39sNBZa4.js} +1 -1
  57. package/dist/ui/assets/{openai-codex-Ds9ud1Aq.js → openai-codex-DUyhVHOH.js} +1 -1
  58. package/dist/ui/assets/{openrouter-WdFvkGuZ.js → openrouter-Dpc-slj8.js} +1 -1
  59. package/dist/ui/assets/{openrouter-zQAB7WS7.js → openrouter-Dt0-tju5.js} +1 -1
  60. package/dist/ui/assets/{openrouter-oauth-BPGyVeXS.js → openrouter-oauth-Bw9klnns.js} +1 -1
  61. package/dist/ui/assets/{openrouter-oauth-_dDmj9RY.js → openrouter-oauth-WcOnlgk3.js} +1 -1
  62. package/dist/ui/assets/panel-rpc-handlers-CRIaZYfR.js +2 -0
  63. package/dist/ui/assets/{panelize-shell-CazjIR5d.js → panelize-shell-C7Ptpa69.js} +1 -1
  64. package/dist/ui/assets/preview-url-BvtKejEp.js +1 -0
  65. package/dist/ui/assets/{provider-RlrlqPhO.js → provider-BKNTonMh.js} +2 -2
  66. package/dist/ui/assets/{provider-SGu61JcF.js → provider-Bc9U8jIG.js} +1 -1
  67. package/dist/ui/assets/provider-settings-9D34aQwf.js +1 -0
  68. package/dist/ui/assets/{provider-settings-CDPm1XM7.js → provider-settings-BdsyHTuR.js} +1 -1
  69. package/dist/ui/assets/{provider-store-access-CzIObh6x.js → provider-store-access-CvRhpMjO.js} +2 -2
  70. package/dist/ui/assets/provider-store-access-Dk0cF7LZ.js +1 -0
  71. package/dist/ui/assets/{providers-CggJCtZV.js → providers-Chea6cTR.js} +1 -1
  72. package/dist/ui/assets/{providers-V2WCWsve.js → providers-gHHjy3ja.js} +2 -2
  73. package/dist/ui/assets/{py-realm-worker-BQGDlUNk.js → py-realm-worker-DGFMfg27.js} +2 -2
  74. package/dist/ui/assets/{pyodide-Cw8OCGih.js → pyodide-BDRAQKP6.js} +2 -2
  75. package/dist/ui/assets/{quick-llm-9ozW69D2.js → quick-llm-Dg39s0LK.js} +1 -1
  76. package/dist/ui/assets/{recovery-screen-DtyG3xtc.js → recovery-screen-B5CPvW63.js} +1 -1
  77. package/dist/ui/assets/rest-ops-31ZkM4IG.js +1 -0
  78. package/dist/ui/assets/rest-ops-D6OyHX5S.js +1 -0
  79. package/dist/ui/assets/{run-BC75hCaQ.js → run-BBAgk3Ml.js} +1 -1
  80. package/dist/ui/assets/{run-DZZ1wNjA.js → run-BMFAdS2O.js} +2 -2
  81. package/dist/ui/assets/{run-Cayg5JY1.js → run-C31PlCiF.js} +1 -1
  82. package/dist/ui/assets/{run-7GOuvq0_.js → run-DZMSwK6o.js} +1 -1
  83. package/dist/ui/assets/{session-freezer-Dts3Ktja.js → session-freezer-DcYjRdMg.js} +3 -3
  84. package/dist/ui/assets/{setup-feature-flags-remote-NgzCq0Su.js → setup-feature-flags-remote-w6i7-xsn.js} +1 -1
  85. package/dist/ui/assets/{setup-standalone-prelude-aT6dC0LG.js → setup-standalone-prelude-DFrkjkWu.js} +1 -1
  86. package/dist/ui/assets/{setup-sudo-CLh8fFfu.js → setup-sudo-C0g6ApHD.js} +2 -2
  87. package/dist/ui/assets/shared-BpOmrBY4.js +1 -0
  88. package/dist/ui/assets/{shared-CEdkXLFL.js → shared-CTdWt5ZU.js} +1 -1
  89. package/dist/ui/assets/{skills-C3pVN-RC.js → skills-DHmzZdlX.js} +1 -1
  90. package/dist/ui/assets/slicc-version-CVpFSZ60.js +1 -0
  91. package/dist/ui/assets/{snapshot-u_rfNldX.js → snapshot-CB063Z3h.js} +1 -1
  92. package/dist/ui/assets/{snapshot-features-CdMjSZ1w.js → snapshot-features-CpPq39XU.js} +1 -1
  93. package/dist/ui/assets/{speak-CAiWCN3J.js → speak-Dx2FEQBZ.js} +1 -1
  94. package/dist/ui/assets/sprinkle-bridge-BVfyHi8X.js +2 -0
  95. package/dist/ui/assets/{sprinkle-manager-BlHamQKs.js → sprinkle-manager-Dxc8U2h1.js} +1 -1
  96. package/dist/ui/assets/{src-CC79QyGZ.js → src-kInvzkEg.js} +29 -0
  97. package/dist/ui/assets/{state-B7nfCBnx.js → state-Ct1GOwP8.js} +1 -1
  98. package/dist/ui/assets/{store-DrtpZOiW.js → store-BiIJ0_As.js} +1 -1
  99. package/dist/ui/assets/{store-DNT00GaF.js → store-IAZCSKrZ.js} +1 -1
  100. package/dist/ui/assets/{sudo-DTiL2bcG.js → sudo-llO15-hr.js} +1 -1
  101. package/dist/ui/assets/transformers-env-DnkszgHP.js +1 -0
  102. package/dist/ui/assets/{upgrade-detection-XL-DbS1T.js → upgrade-detection-D4-saTZd.js} +1 -1
  103. package/dist/ui/assets/{voice-reply-CF-palsG.js → voice-reply-BGR2cNZ_.js} +1 -1
  104. package/dist/ui/assets/{wc-attach-BCVB22e6.js → wc-attach-BLZapYO1.js} +2 -2
  105. package/dist/ui/assets/{wc-extension-CT5O1Bp9.js → wc-extension-BKv2ap0T.js} +2 -2
  106. package/dist/ui/assets/{wc-float-label-BpVboxDs.js → wc-float-label-Ck2cAgp3.js} +1 -1
  107. package/dist/ui/assets/wc-follower-DEUkZTu_.js +17 -0
  108. package/dist/ui/assets/wc-follower-oauth-CTgC7D7W.js +5 -0
  109. package/dist/ui/assets/wc-live-DkTXA2Bw.js +8 -0
  110. package/dist/ui/assets/{wc-nav-CTfWCa-k.js → wc-nav-2fZPqw9y.js} +2 -2
  111. package/dist/ui/assets/{wc-onboarding-Ddwx2pAn.js → wc-onboarding-C8kBkCKD.js} +2 -2
  112. package/dist/ui/assets/{wc-placeholder-CB-nwwrA.js → wc-placeholder-CzEI-8XA.js} +2 -2
  113. package/dist/ui/assets/{wc-settings-C-ZxtQA4.js → wc-settings--cPEMbHv.js} +2 -2
  114. package/dist/ui/assets/wc-shell-58xXdR-i.js +122 -0
  115. package/dist/ui/assets/{wc-sprinkles-BDiQQ8lz.js → wc-sprinkles-CN5LWTpu.js} +2 -2
  116. package/dist/ui/assets/wc-tray-D--vd5uV.js +5 -0
  117. package/dist/ui/assets/whisper-session-i96-W77q.js +2 -0
  118. package/dist/ui/assets/{xai-grok-mZX4CDCC.js → xai-grok-0dg6iolO.js} +1 -1
  119. package/dist/ui/assets/{xai-grok-Bnt5giBD.js → xai-grok-pGpFfbjv.js} +1 -1
  120. package/dist/ui/index.html +6 -6
  121. package/dist/ui/llm-proxy-sw.js +1 -1
  122. package/dist/ui/packages/webapp/index.html +6 -6
  123. package/dist/ui/preview-sw.js +1 -1
  124. package/package.json +1 -1
  125. package/dist/ui/assets/CLAUDE-Bio4HPGK.js +0 -1
  126. package/dist/ui/assets/__vite-browser-external-CIVB95Zz.js +0 -1
  127. package/dist/ui/assets/panel-rpc-handlers-BQmhUbHq.js +0 -2
  128. package/dist/ui/assets/provider-settings-CMSS1i60.js +0 -1
  129. package/dist/ui/assets/provider-store-access-TwQNg4j0.js +0 -1
  130. package/dist/ui/assets/rest-ops-BRBsjYZs.js +0 -1
  131. package/dist/ui/assets/rest-ops-CWNG3-Sx.js +0 -1
  132. package/dist/ui/assets/shared-BapVZGtv.js +0 -1
  133. package/dist/ui/assets/slicc-version-DxNGGaVc.js +0 -1
  134. package/dist/ui/assets/sprinkle-bridge-nsMZiNm5.js +0 -2
  135. package/dist/ui/assets/transformers-env-4p1QMDFU.js +0 -1
  136. package/dist/ui/assets/wc-follower-CVbDzcUg.js +0 -17
  137. package/dist/ui/assets/wc-follower-oauth-CmRMrB5f.js +0 -5
  138. package/dist/ui/assets/wc-live-CGxuI_qJ.js +0 -8
  139. package/dist/ui/assets/wc-shell-7EUoAJ_c.js +0 -122
  140. package/dist/ui/assets/wc-tray-NNzNaeIr.js +0 -5
  141. package/dist/ui/assets/whisper-session-BTUdcXlN.js +0 -2
package/README.md CHANGED
@@ -11,7 +11,7 @@ If this scares, confuses, or excites you, keep reading.
11
11
 
12
12
  # slicc — Self-Licking Ice Cream Cone
13
13
 
14
- [![51% Vibe_Coded](https://img.shields.io/badge/51%25-Vibe_Coded-ff69b4?style=for-the-badge&logo=claude&logoColor=white)](https://github.com/ai-ecoverse/vibe-coded-badge-action)
14
+ [![53% Vibe_Coded](https://img.shields.io/badge/53%25-Vibe_Coded-ff69b4?style=for-the-badge&logo=claude&logoColor=white)](https://github.com/ai-ecoverse/vibe-coded-badge-action)
15
15
 
16
16
  [![npm](https://img.shields.io/npm/v/sliccy)](https://www.npmjs.com/package/sliccy)
17
17
 
@@ -12,10 +12,32 @@ export interface SecretEnvEntry {
12
12
  export declare function parseDomainsCsv(raw: string): string[];
13
13
  /** Pair values with non-empty domain companions, preserving first-name order. */
14
14
  export declare function pairEnvEntriesToSecrets(entries: EnvEntry[]): SecretEnvEntry[];
15
+ /**
16
+ * Secret values are stored one per line, so a value carrying a line break
17
+ * cannot survive a `serialize` → `parse` round-trip: `serializeEnvFile` quotes
18
+ * it with a real LF inside the quotes and `parseEnvFile` then splits on `\n`,
19
+ * keeping the unbalanced first line and dropping the rest. Writers reject such
20
+ * a value at the boundary instead of silently truncating it (issue #2828).
21
+ *
22
+ * Mirrored by `EnvFileFormat.isSingleLineValue` in
23
+ * `packages/swift-server/Sources/Keychain/EnvFileFormat.swift`; both sides are
24
+ * pinned by the cross-implementation vectors.
25
+ */
26
+ export declare function isSingleLineSecretValue(value: string): boolean;
27
+ /** The single rejection message both servers return for a multiline value. */
28
+ export declare function multilineSecretValueError(name: string): string;
15
29
  /** Parse the shared, deliberately small .env subset used for secret storage. */
16
30
  export declare function parseEnvFile(content: string): EnvEntry[];
17
31
  /** Parse assignment lines while preserving every character after the first `=`. */
18
32
  export declare function parseEnvFilePreservingValues(content: string): EnvEntry[];
33
+ /**
34
+ * Serialize entries to the line-oriented `.env` subset.
35
+ *
36
+ * Throws on a value containing a line break rather than emitting a blob that
37
+ * would parse back truncated. This is the fail-closed backstop behind the
38
+ * boundary checks in the two privileged servers' secret routes — no write path
39
+ * can corrupt the store by going around them.
40
+ */
19
41
  export declare function serializeEnvFile(entries: EnvEntry[]): string;
20
42
  /** Profile name validation shared by every S3 secret-schema writer. */
21
43
  export declare const PROFILE_RE: RegExp;
@@ -1 +1 @@
1
- {"version":3,"file":"secret-env-schema.d.ts","sourceRoot":"","sources":["../src/secret-env-schema.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAErE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAKrD;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,cAAc,EAAE,CAsB7E;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmBxD;AAED,mFAAmF;AACnF,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAYxE;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAO5D;AAED,uEAAuE;AACvE,eAAO,MAAM,UAAU,QAAsB,CAAC;AAE9C,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAStE;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,mBAAmB,CASjF"}
1
+ {"version":3,"file":"secret-env-schema.d.ts","sourceRoot":"","sources":["../src/secret-env-schema.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAErE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAKrD;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,cAAc,EAAE,CAsB7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmBxD;AAED,mFAAmF;AACnF,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAYxE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAQ5D;AAED,uEAAuE;AACvE,eAAO,MAAM,UAAU,QAAsB,CAAC;AAE9C,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAStE;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,mBAAmB,CASjF"}
@@ -31,6 +31,24 @@ export function pairEnvEntriesToSecrets(entries) {
31
31
  : [];
32
32
  });
33
33
  }
34
+ /**
35
+ * Secret values are stored one per line, so a value carrying a line break
36
+ * cannot survive a `serialize` → `parse` round-trip: `serializeEnvFile` quotes
37
+ * it with a real LF inside the quotes and `parseEnvFile` then splits on `\n`,
38
+ * keeping the unbalanced first line and dropping the rest. Writers reject such
39
+ * a value at the boundary instead of silently truncating it (issue #2828).
40
+ *
41
+ * Mirrored by `EnvFileFormat.isSingleLineValue` in
42
+ * `packages/swift-server/Sources/Keychain/EnvFileFormat.swift`; both sides are
43
+ * pinned by the cross-implementation vectors.
44
+ */
45
+ export function isSingleLineSecretValue(value) {
46
+ return !/[\n\r]/.test(value);
47
+ }
48
+ /** The single rejection message both servers return for a multiline value. */
49
+ export function multilineSecretValueError(name) {
50
+ return `Secret "${name}" value cannot contain newlines; the secret store is line-oriented and would truncate it to the first line`;
51
+ }
34
52
  /** Parse the shared, deliberately small .env subset used for secret storage. */
35
53
  export function parseEnvFile(content) {
36
54
  const entries = [];
@@ -68,8 +86,18 @@ export function parseEnvFilePreservingValues(content) {
68
86
  }
69
87
  return entries;
70
88
  }
89
+ /**
90
+ * Serialize entries to the line-oriented `.env` subset.
91
+ *
92
+ * Throws on a value containing a line break rather than emitting a blob that
93
+ * would parse back truncated. This is the fail-closed backstop behind the
94
+ * boundary checks in the two privileged servers' secret routes — no write path
95
+ * can corrupt the store by going around them.
96
+ */
71
97
  export function serializeEnvFile(entries) {
72
98
  const lines = entries.map(({ key, value }) => {
99
+ if (!isSingleLineSecretValue(value))
100
+ throw new Error(multilineSecretValueError(key));
73
101
  const needsQuoting = /[\s#"']/.test(value);
74
102
  const serialized = needsQuoting ? `"${value.replace(/"/g, '\\"')}"` : value;
75
103
  return `${key}=${serialized}`;
@@ -1 +1 @@
1
- {"version":3,"file":"secret-env-schema.js","sourceRoot":"","sources":["../src/secret-env-schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"secret-env-schema.js","sourceRoot":"","sources":["../src/secret-env-schema.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { previewSecret } from '../_shared/index.js';
1
+ import { isSingleLineSecretValue, multilineSecretValueError, previewSecret, } from '../_shared/index.js';
2
2
  import express from 'express';
3
3
  import { handleDaSignAndForward, handleS3SignAndForward } from '../secrets/sign-and-forward.js';
4
4
  /**
@@ -41,6 +41,12 @@ async function handleScopeEdit(res, name, domains, secretStore, secretProxy) {
41
41
  const existing = secretStore.get(name);
42
42
  if (!existing)
43
43
  return res.status(404).json({ error: `no secret named "${name}"` });
44
+ // Re-saving is a full rewrite of the value, so a value the store should
45
+ // never have held (a hand-edited multiline entry) must not be laundered
46
+ // through a scope change into a truncated one (#2828).
47
+ if (!isSingleLineSecretValue(existing.value)) {
48
+ return res.status(400).json({ error: multilineSecretValueError(name) });
49
+ }
44
50
  secretStore.set(name, existing.value, domains);
45
51
  }
46
52
  await secretProxy.reload();
@@ -122,6 +128,13 @@ export function registerSecretRoutes(app, deps) {
122
128
  if (typeof name !== 'string' || typeof value !== 'string' || !isStringArray(domains)) {
123
129
  return res.status(400).json({ error: 'bad-request' });
124
130
  }
131
+ // Fail closed at the boundary: the persisted .env schema stores one value
132
+ // per line, so a multiline value (a PEM private key, typically) would be
133
+ // truncated to its first line — and an overwrite would replace a working
134
+ // credential with an unrecoverable one behind a 200 (#2828).
135
+ if (!isSingleLineSecretValue(value)) {
136
+ return res.status(400).json({ error: multilineSecretValueError(name) });
137
+ }
125
138
  try {
126
139
  secretStore.set(name, value, domains);
127
140
  await secretProxy.reload();
@@ -9,7 +9,7 @@
9
9
  import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
10
10
  import { homedir } from 'node:os';
11
11
  import { dirname, resolve } from 'node:path';
12
- import { DOMAINS_SUFFIX, pairEnvEntriesToSecrets } from '../_shared/index.js';
12
+ import { DOMAINS_SUFFIX, isSingleLineSecretValue, multilineSecretValueError, pairEnvEntriesToSecrets, } from '../_shared/index.js';
13
13
  import { parseEnvFile, serializeEnvFile } from './env-file.js';
14
14
  const DEFAULT_PATH = resolve(homedir(), '.slicc', 'secrets.env');
15
15
  const FILE_MODE = 0o600;
@@ -25,6 +25,12 @@ export class EnvSecretStore {
25
25
  if (domains.length === 0) {
26
26
  throw new Error(`Secret "${name}" must have at least one authorized domain`);
27
27
  }
28
+ // The .env schema is line-oriented, so a multiline value would be written
29
+ // back truncated to its first line (#2828). Refuse the write rather than
30
+ // report success over a corrupted credential.
31
+ if (!isSingleLineSecretValue(value)) {
32
+ throw new Error(multilineSecretValueError(name));
33
+ }
28
34
  const entries = this.readEntries();
29
35
  const domainsKey = name + DOMAINS_SUFFIX;
30
36
  upsertEntry(entries, name, value);