@tinyviber/a2h 0.1.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.
Files changed (162) hide show
  1. package/README.md +229 -0
  2. package/bin/a2h.js +7 -0
  3. package/dist/actions/engine.d.ts +40 -0
  4. package/dist/actions/engine.d.ts.map +1 -0
  5. package/dist/actions/engine.js +227 -0
  6. package/dist/actions/engine.js.map +1 -0
  7. package/dist/actions/policy.d.ts +8 -0
  8. package/dist/actions/policy.d.ts.map +1 -0
  9. package/dist/actions/policy.js +55 -0
  10. package/dist/actions/policy.js.map +1 -0
  11. package/dist/cli/index.d.ts +2 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +258 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/init.d.ts +8 -0
  16. package/dist/cli/init.d.ts.map +1 -0
  17. package/dist/cli/init.js +69 -0
  18. package/dist/cli/init.js.map +1 -0
  19. package/dist/index.d.ts +26 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +66 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/ir/build.d.ts +8 -0
  24. package/dist/ir/build.d.ts.map +1 -0
  25. package/dist/ir/build.js +397 -0
  26. package/dist/ir/build.js.map +1 -0
  27. package/dist/ir/enrich.d.ts +21 -0
  28. package/dist/ir/enrich.d.ts.map +1 -0
  29. package/dist/ir/enrich.js +177 -0
  30. package/dist/ir/enrich.js.map +1 -0
  31. package/dist/ir/identity.d.ts +3 -0
  32. package/dist/ir/identity.d.ts.map +1 -0
  33. package/dist/ir/identity.js +91 -0
  34. package/dist/ir/identity.js.map +1 -0
  35. package/dist/ir/scoring.d.ts +7 -0
  36. package/dist/ir/scoring.d.ts.map +1 -0
  37. package/dist/ir/scoring.js +88 -0
  38. package/dist/ir/scoring.js.map +1 -0
  39. package/dist/parsers/code.d.ts +3 -0
  40. package/dist/parsers/code.d.ts.map +1 -0
  41. package/dist/parsers/code.js +17 -0
  42. package/dist/parsers/code.js.map +1 -0
  43. package/dist/parsers/diff.d.ts +11 -0
  44. package/dist/parsers/diff.d.ts.map +1 -0
  45. package/dist/parsers/diff.js +125 -0
  46. package/dist/parsers/diff.js.map +1 -0
  47. package/dist/parsers/json.d.ts +3 -0
  48. package/dist/parsers/json.d.ts.map +1 -0
  49. package/dist/parsers/json.js +16 -0
  50. package/dist/parsers/json.js.map +1 -0
  51. package/dist/parsers/log.d.ts +3 -0
  52. package/dist/parsers/log.d.ts.map +1 -0
  53. package/dist/parsers/log.js +105 -0
  54. package/dist/parsers/log.js.map +1 -0
  55. package/dist/parsers/markdown.d.ts +15 -0
  56. package/dist/parsers/markdown.d.ts.map +1 -0
  57. package/dist/parsers/markdown.js +89 -0
  58. package/dist/parsers/markdown.js.map +1 -0
  59. package/dist/parsers/text.d.ts +24 -0
  60. package/dist/parsers/text.d.ts.map +1 -0
  61. package/dist/parsers/text.js +98 -0
  62. package/dist/parsers/text.js.map +1 -0
  63. package/dist/presentation/blocks.d.ts +14 -0
  64. package/dist/presentation/blocks.d.ts.map +1 -0
  65. package/dist/presentation/blocks.js +87 -0
  66. package/dist/presentation/blocks.js.map +1 -0
  67. package/dist/presentation/present.d.ts +12 -0
  68. package/dist/presentation/present.d.ts.map +1 -0
  69. package/dist/presentation/present.js +115 -0
  70. package/dist/presentation/present.js.map +1 -0
  71. package/dist/providers/mock.d.ts +3 -0
  72. package/dist/providers/mock.d.ts.map +1 -0
  73. package/dist/providers/mock.js +171 -0
  74. package/dist/providers/mock.js.map +1 -0
  75. package/dist/providers/registry.d.ts +12 -0
  76. package/dist/providers/registry.d.ts.map +1 -0
  77. package/dist/providers/registry.js +20 -0
  78. package/dist/providers/registry.js.map +1 -0
  79. package/dist/providers/types.d.ts +64 -0
  80. package/dist/providers/types.d.ts.map +1 -0
  81. package/dist/providers/types.js +40 -0
  82. package/dist/providers/types.js.map +1 -0
  83. package/dist/renderer/assets/index.html +41 -0
  84. package/dist/renderer/assets/js/actions.js +205 -0
  85. package/dist/renderer/assets/js/api.js +75 -0
  86. package/dist/renderer/assets/js/artifacts.js +422 -0
  87. package/dist/renderer/assets/js/blocks.js +301 -0
  88. package/dist/renderer/assets/js/bus.js +21 -0
  89. package/dist/renderer/assets/js/cards.js +137 -0
  90. package/dist/renderer/assets/js/dom.js +61 -0
  91. package/dist/renderer/assets/js/format.js +118 -0
  92. package/dist/renderer/assets/js/main.js +125 -0
  93. package/dist/renderer/assets/js/nav.js +80 -0
  94. package/dist/renderer/assets/js/router.js +64 -0
  95. package/dist/renderer/assets/js/store.js +69 -0
  96. package/dist/renderer/assets/js/views.js +356 -0
  97. package/dist/renderer/assets/styles.css +856 -0
  98. package/dist/renderer/content.d.ts +11 -0
  99. package/dist/renderer/content.d.ts.map +1 -0
  100. package/dist/renderer/content.js +114 -0
  101. package/dist/renderer/content.js.map +1 -0
  102. package/dist/scanner/classify.d.ts +17 -0
  103. package/dist/scanner/classify.d.ts.map +1 -0
  104. package/dist/scanner/classify.js +117 -0
  105. package/dist/scanner/classify.js.map +1 -0
  106. package/dist/scanner/git.d.ts +3 -0
  107. package/dist/scanner/git.d.ts.map +1 -0
  108. package/dist/scanner/git.js +43 -0
  109. package/dist/scanner/git.js.map +1 -0
  110. package/dist/scanner/ignore.d.ts +22 -0
  111. package/dist/scanner/ignore.d.ts.map +1 -0
  112. package/dist/scanner/ignore.js +57 -0
  113. package/dist/scanner/ignore.js.map +1 -0
  114. package/dist/scanner/scan.d.ts +8 -0
  115. package/dist/scanner/scan.d.ts.map +1 -0
  116. package/dist/scanner/scan.js +108 -0
  117. package/dist/scanner/scan.js.map +1 -0
  118. package/dist/security/boundary.d.ts +19 -0
  119. package/dist/security/boundary.d.ts.map +1 -0
  120. package/dist/security/boundary.js +59 -0
  121. package/dist/security/boundary.js.map +1 -0
  122. package/dist/security/urlPolicy.d.ts +4 -0
  123. package/dist/security/urlPolicy.d.ts.map +1 -0
  124. package/dist/security/urlPolicy.js +56 -0
  125. package/dist/security/urlPolicy.js.map +1 -0
  126. package/dist/security/workspaceRead.d.ts +25 -0
  127. package/dist/security/workspaceRead.d.ts.map +1 -0
  128. package/dist/security/workspaceRead.js +103 -0
  129. package/dist/security/workspaceRead.js.map +1 -0
  130. package/dist/semantics/load.d.ts +11 -0
  131. package/dist/semantics/load.d.ts.map +1 -0
  132. package/dist/semantics/load.js +537 -0
  133. package/dist/semantics/load.js.map +1 -0
  134. package/dist/semantics/resolve.d.ts +37 -0
  135. package/dist/semantics/resolve.d.ts.map +1 -0
  136. package/dist/semantics/resolve.js +211 -0
  137. package/dist/semantics/resolve.js.map +1 -0
  138. package/dist/semantics/types.d.ts +158 -0
  139. package/dist/semantics/types.d.ts.map +1 -0
  140. package/dist/semantics/types.js +3 -0
  141. package/dist/semantics/types.js.map +1 -0
  142. package/dist/server/server.d.ts +17 -0
  143. package/dist/server/server.d.ts.map +1 -0
  144. package/dist/server/server.js +510 -0
  145. package/dist/server/server.js.map +1 -0
  146. package/dist/server/workspace.d.ts +53 -0
  147. package/dist/server/workspace.d.ts.map +1 -0
  148. package/dist/server/workspace.js +211 -0
  149. package/dist/server/workspace.js.map +1 -0
  150. package/dist/types.d.ts +570 -0
  151. package/dist/types.d.ts.map +1 -0
  152. package/dist/types.js +19 -0
  153. package/dist/types.js.map +1 -0
  154. package/dist/util/path.d.ts +11 -0
  155. package/dist/util/path.d.ts.map +1 -0
  156. package/dist/util/path.js +35 -0
  157. package/dist/util/path.js.map +1 -0
  158. package/dist/util/safeRead.d.ts +38 -0
  159. package/dist/util/safeRead.d.ts.map +1 -0
  160. package/dist/util/safeRead.js +117 -0
  161. package/dist/util/safeRead.js.map +1 -0
  162. package/package.json +40 -0
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isWithin = isWithin;
4
+ exports.resolveRelPath = resolveRelPath;
5
+ exports.resolveRealPath = resolveRealPath;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const path_1 = require("../util/path");
9
+ // Path containment primitives. These are pure predicates over the filesystem —
10
+ // they read metadata only, never content.
11
+ //
12
+ // They live outside `server/` because containment is not an HTTP concern: a
13
+ // workspace-relative path can arrive from a URL, from a manifest, or from a
14
+ // block, and every one of those routes has to answer the same question.
15
+ function isWithin(root, target) {
16
+ const r = (0, node_path_1.resolve)(root);
17
+ const t = (0, node_path_1.resolve)(target);
18
+ return t === r || t.startsWith(r + node_path_1.sep);
19
+ }
20
+ /**
21
+ * Resolves a workspace-relative POSIX path to an absolute path strictly
22
+ * inside the workspace root. Returns null on traversal attempts.
23
+ *
24
+ * This is a lexical check: it refuses `..`, but says nothing about symlinks.
25
+ * Use `resolveRealPath` when the answer must survive a link in the tree.
26
+ */
27
+ function resolveRelPath(root, rel) {
28
+ const normalized = (0, path_1.normalizeRel)(rel);
29
+ if (normalized === null)
30
+ return null;
31
+ const abs = (0, node_path_1.resolve)(root, normalized);
32
+ if (!isWithin(root, abs))
33
+ return null;
34
+ return abs;
35
+ }
36
+ /**
37
+ * Like resolveRelPath, but also resolves symlinks and verifies the *real* path
38
+ * stays inside the workspace. This is the check that catches an intermediate
39
+ * directory which is a link out of the tree — `leak -> /etc` makes
40
+ * `leak/passwd` lexical, but never contained.
41
+ *
42
+ * Returns the lexical absolute path (stable, knows no symlinks) on success.
43
+ */
44
+ function resolveRealPath(root, rel) {
45
+ const abs = resolveRelPath(root, rel);
46
+ if (abs === null)
47
+ return null;
48
+ try {
49
+ const realRoot = (0, node_fs_1.realpathSync)(root);
50
+ const real = (0, node_fs_1.realpathSync)(abs);
51
+ if (!isWithin(realRoot, real))
52
+ return null;
53
+ return abs;
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ }
59
+ //# sourceMappingURL=boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../src/security/boundary.ts"],"names":[],"mappings":";;AAWA,4BAIC;AASD,wCAMC;AAUD,0CAWC;AAnDD,qCAAuC;AACvC,yCAAyC;AACzC,uCAA4C;AAE5C,+EAA+E;AAC/E,0CAA0C;AAC1C,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,wEAAwE;AAExE,SAAgB,QAAQ,CAAC,IAAY,EAAE,MAAc;IACnD,MAAM,CAAC,GAAG,IAAA,mBAAO,EAAC,IAAI,CAAC,CAAC;IACxB,MAAM,CAAC,GAAG,IAAA,mBAAO,EAAC,MAAM,CAAC,CAAC;IAC1B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,eAAG,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,IAAY,EAAE,GAAW;IACtD,MAAM,UAAU,GAAG,IAAA,mBAAY,EAAC,GAAG,CAAC,CAAC;IACrC,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,GAAG,GAAG,IAAA,mBAAO,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,eAAe,CAAC,IAAY,EAAE,GAAW;IACvD,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACtC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAA,sBAAY,EAAC,IAAI,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,IAAA,sBAAY,EAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function isSafeLinkHref(href: unknown): href is string;
2
+ /** True when the value is a workspace-relative path (no scheme, no `//`). */
3
+ export declare function isWorkspaceRelative(value: string): boolean;
4
+ //# sourceMappingURL=urlPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urlPolicy.d.ts","sourceRoot":"","sources":["../../src/security/urlPolicy.ts"],"names":[],"mappings":"AAgCA,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM,CAQ5D;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAI1D"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isSafeLinkHref = isSafeLinkHref;
4
+ exports.isWorkspaceRelative = isWorkspaceRelative;
5
+ /**
6
+ * One answer to "may this href be rendered as a link?", for every layer that
7
+ * writes one.
8
+ *
9
+ * Producer-authored JSON reaches the page as a link target in more than one
10
+ * place (list items today, and whatever block kind is added next), and the
11
+ * renderer is not the only consumer: a link that survives server-side
12
+ * normalisation is also what any future consumer will see. So the policy is a
13
+ * small shared allowlist rather than a check inside the one renderer we
14
+ * happened to write today.
15
+ *
16
+ * Allowed:
17
+ * - `http:` / `https:` / `mailto:` — the same three the markdown renderer
18
+ * validates links against, so a workspace has one URL policy, not two
19
+ * - `#...` — an in-page anchor
20
+ * - a workspace-relative path — routed as an artifact
21
+ *
22
+ * Everything else is refused. `javascript:` and `vbscript:` are script
23
+ * execution; `data:` and `blob:` are document injection; `file:` reaches the
24
+ * local disk. That the page's CSP would also stop some of them is not a reason
25
+ * to pass them through — a second line of defence is not a first one.
26
+ *
27
+ * Protocol-relative (`//host/path`) is refused as well: it is an off-site
28
+ * navigation wearing a path's clothes.
29
+ */
30
+ const SAFE_LINK_SCHEMES = /^(?:https?|mailto):/i;
31
+ const HAS_SCHEME = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
32
+ function hasScheme(value) {
33
+ return HAS_SCHEME.test(value);
34
+ }
35
+ function isSafeLinkHref(href) {
36
+ if (typeof href !== 'string')
37
+ return false;
38
+ const value = href.trim();
39
+ if (value === '')
40
+ return false;
41
+ if (value.startsWith('#'))
42
+ return true;
43
+ if (value.startsWith('//'))
44
+ return false;
45
+ if (hasScheme(value))
46
+ return SAFE_LINK_SCHEMES.test(value);
47
+ return true;
48
+ }
49
+ /** True when the value is a workspace-relative path (no scheme, no `//`). */
50
+ function isWorkspaceRelative(value) {
51
+ const trimmed = value.trim();
52
+ if (trimmed === '' || trimmed.startsWith('//'))
53
+ return false;
54
+ return !hasScheme(trimmed);
55
+ }
56
+ //# sourceMappingURL=urlPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urlPolicy.js","sourceRoot":"","sources":["../../src/security/urlPolicy.ts"],"names":[],"mappings":";;AAgCA,wCAQC;AAGD,kDAIC;AA/CD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AACjD,MAAM,UAAU,GAAG,2BAA2B,CAAC;AAE/C,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,SAAgB,cAAc,CAAC,IAAa;IAC1C,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC1B,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,SAAS,CAAC,KAAK,CAAC;QAAE,OAAO,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6EAA6E;AAC7E,SAAgB,mBAAmB,CAAC,KAAa;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7D,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { FileEntry } from '../types';
2
+ /** Default cap for one producer-authored text read. */
3
+ export declare const DEFAULT_MAX_TEXT_BYTES: number;
4
+ /** Hard ceiling. A caller may ask for less, never for more. */
5
+ export declare const MAX_TEXT_BYTES_CEILING: number;
6
+ export interface WorkspaceReaderOptions {
7
+ rootDir: string;
8
+ /** The scanner's index. Its contents are the allowlist. */
9
+ files: readonly FileEntry[];
10
+ }
11
+ export interface WorkspaceReader {
12
+ /** True when `rel` names a file the scanner indexed. */
13
+ has(rel: string): boolean;
14
+ /** True when `rel` is a scanned file that may be read as text. */
15
+ canReadText(rel: string): boolean;
16
+ /**
17
+ * Reads a workspace-relative text file. Returns undefined unless the path
18
+ * passed every check above. An empty file reads as an empty string.
19
+ */
20
+ readText(rel: string, maxBytes?: number): string | undefined;
21
+ /** True when `rel` is a scanned image that may be served to the browser. */
22
+ canReadImage(rel: string): boolean;
23
+ }
24
+ export declare function createWorkspaceReader(options: WorkspaceReaderOptions): WorkspaceReader;
25
+ //# sourceMappingURL=workspaceRead.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaceRead.d.ts","sourceRoot":"","sources":["../../src/security/workspaceRead.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAmC1C,uDAAuD;AACvD,eAAO,MAAM,sBAAsB,QAAa,CAAC;AAEjD,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB,QAAkB,CAAC;AAEtD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,kEAAkE;IAClE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,4EAA4E;IAC5E,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAsDtF"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_TEXT_BYTES_CEILING = exports.DEFAULT_MAX_TEXT_BYTES = void 0;
4
+ exports.createWorkspaceReader = createWorkspaceReader;
5
+ const classify_1 = require("../scanner/classify");
6
+ const path_1 = require("../util/path");
7
+ const safeRead_1 = require("../util/safeRead");
8
+ const boundary_1 = require("./boundary");
9
+ // ---------------------------------------------------------------------------
10
+ // The workspace read capability.
11
+ //
12
+ // Producer-authored content can name any path it likes: a markdown block, an
13
+ // image reference, a relation target, a future protocol extension. This module
14
+ // is the one place such a name is turned into bytes.
15
+ //
16
+ // The point is not that these five checks are individually clever — it is that
17
+ // they live here rather than at each call site. A caller cannot forget them,
18
+ // and a new protocol feature gets them for free by asking for a reader instead
19
+ // of for a path.
20
+ //
21
+ // 1. the path must name a file the scanner indexed (the allowlist — a path
22
+ // the scanner skipped was skipped for a reason);
23
+ // 2. it must not be sensitive (a secret is a secret regardless of who is
24
+ // asking nicely);
25
+ // 3. it must not be a symlink (the scanner already refuses to descend into
26
+ // one, and a link whose name looks like markdown must never become a read
27
+ // primitive);
28
+ // 4. its *real* path must still be inside the workspace — this is the check
29
+ // that catches an intermediate directory that is a link out of the tree,
30
+ // which lexical normalization cannot see;
31
+ // 5. the read is bounded, with a ceiling the caller cannot raise.
32
+ //
33
+ // Checks 1–3 are properties of the scanner's entry. 4 and 5 are re-derived
34
+ // here, per call, because a capability that trusts its caller's earlier
35
+ // checking is not a boundary.
36
+ // ---------------------------------------------------------------------------
37
+ /** Default cap for one producer-authored text read. */
38
+ exports.DEFAULT_MAX_TEXT_BYTES = 512 * 1024;
39
+ /** Hard ceiling. A caller may ask for less, never for more. */
40
+ exports.MAX_TEXT_BYTES_CEILING = 4 * 1024 * 1024;
41
+ function createWorkspaceReader(options) {
42
+ const { rootDir } = options;
43
+ const byPath = new Map();
44
+ for (const file of options.files)
45
+ byPath.set(file.path, file);
46
+ function entryFor(rel) {
47
+ const normalized = (0, path_1.normalizeRel)(rel);
48
+ if (normalized === null || normalized === '')
49
+ return undefined;
50
+ return byPath.get(normalized);
51
+ }
52
+ /**
53
+ * The shared gate. `resolveRealPath` runs on every call rather than once at
54
+ * scan time: a directory in the tree can be replaced with a link after the
55
+ * scan, and the containment answer has to be current when the bytes move.
56
+ *
57
+ * Sensitivity is re-derived from the name rather than read off the index's
58
+ * flag alone. The flag is the scanner's answer, and the scanner is good at
59
+ * this — but "the index says it is fine" is precisely the kind of claim a
60
+ * boundary should not have to take on faith.
61
+ */
62
+ function allows(entry, want) {
63
+ if (!entry)
64
+ return false;
65
+ if (entry.sensitive)
66
+ return false;
67
+ if ((0, classify_1.classifyFile)(basename(entry.path)).sensitive)
68
+ return false;
69
+ if (entry.isSymlink)
70
+ return false;
71
+ if (want === 'image' && (0, classify_1.contentKindOf)(entry.kind) !== 'image')
72
+ return false;
73
+ return (0, boundary_1.resolveRealPath)(rootDir, entry.path) !== null;
74
+ }
75
+ return {
76
+ has(rel) {
77
+ return entryFor(rel) !== undefined;
78
+ },
79
+ canReadText(rel) {
80
+ return allows(entryFor(rel), 'text');
81
+ },
82
+ readText(rel, maxBytes = exports.DEFAULT_MAX_TEXT_BYTES) {
83
+ const entry = entryFor(rel);
84
+ if (!allows(entry, 'text'))
85
+ return undefined;
86
+ const abs = (0, boundary_1.resolveRealPath)(rootDir, entry.path);
87
+ if (abs === null)
88
+ return undefined;
89
+ const cap = Math.min(Math.max(0, maxBytes), exports.MAX_TEXT_BYTES_CEILING);
90
+ const read = (0, safeRead_1.readFileNoFollow)(abs, cap);
91
+ return read?.text;
92
+ },
93
+ canReadImage(rel) {
94
+ return allows(entryFor(rel), 'image');
95
+ },
96
+ };
97
+ }
98
+ /** The last segment of a workspace-relative path. */
99
+ function basename(rel) {
100
+ const slash = rel.lastIndexOf('/');
101
+ return slash === -1 ? rel : rel.slice(slash + 1);
102
+ }
103
+ //# sourceMappingURL=workspaceRead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaceRead.js","sourceRoot":"","sources":["../../src/security/workspaceRead.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { FrontmatterSemantics, LoadedSemantics } from './types';
2
+ export declare const MANIFEST_DIR = ".a2h";
3
+ export interface LoadOptions {
4
+ /** Workspace-relative paths of markdown files worth inspecting. */
5
+ markdownPaths?: string[];
6
+ }
7
+ export declare function loadSemantics(rootDir: string, options?: LoadOptions): LoadedSemantics;
8
+ export declare function hasExplicitSemantics(loaded: LoadedSemantics): boolean;
9
+ /** Recognised frontmatter keys, all prefixed to avoid clashing with prose. */
10
+ export declare function readFrontmatterSemantics(absPath: string): FrontmatterSemantics | undefined;
11
+ //# sourceMappingURL=load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/semantics/load.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,oBAAoB,EAGpB,eAAe,EAIhB,MAAM,SAAS,CAAC;AAsBjB,eAAO,MAAM,YAAY,SAAS,CAAC;AAUnC,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,eAAe,CAiDzF;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAErE;AAuID,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAwC1F"}