deepadb 1.1.1 → 1.1.3

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 (230) hide show
  1. package/LICENSE +202 -21
  2. package/NOTICE +16 -0
  3. package/README.md +55 -29
  4. package/build/bridge/adb-bridge.d.ts +14 -0
  5. package/build/bridge/adb-bridge.d.ts.map +1 -1
  6. package/build/bridge/adb-bridge.js +34 -5
  7. package/build/bridge/adb-bridge.js.map +1 -1
  8. package/build/bridge/device-manager.d.ts.map +1 -1
  9. package/build/bridge/device-manager.js +18 -9
  10. package/build/bridge/device-manager.js.map +1 -1
  11. package/build/bridge/local-bridge.d.ts +10 -2
  12. package/build/bridge/local-bridge.d.ts.map +1 -1
  13. package/build/bridge/local-bridge.js +51 -16
  14. package/build/bridge/local-bridge.js.map +1 -1
  15. package/build/config/config.d.ts +7 -0
  16. package/build/config/config.d.ts.map +1 -1
  17. package/build/config/config.js +26 -10
  18. package/build/config/config.js.map +1 -1
  19. package/build/graphql-api.d.ts.map +1 -1
  20. package/build/graphql-api.js +73 -3
  21. package/build/graphql-api.js.map +1 -1
  22. package/build/http-transport.d.ts.map +1 -1
  23. package/build/http-transport.js +44 -7
  24. package/build/http-transport.js.map +1 -1
  25. package/build/index.d.ts.map +1 -1
  26. package/build/index.js +41 -16
  27. package/build/index.js.map +1 -1
  28. package/build/middleware/auth.d.ts.map +1 -1
  29. package/build/middleware/auth.js +43 -6
  30. package/build/middleware/auth.js.map +1 -1
  31. package/build/middleware/chipset.d.ts.map +1 -1
  32. package/build/middleware/chipset.js +16 -1
  33. package/build/middleware/chipset.js.map +1 -1
  34. package/build/middleware/cleanup.d.ts +39 -7
  35. package/build/middleware/cleanup.d.ts.map +1 -1
  36. package/build/middleware/cleanup.js +148 -16
  37. package/build/middleware/cleanup.js.map +1 -1
  38. package/build/middleware/fetch-utils.d.ts +50 -7
  39. package/build/middleware/fetch-utils.d.ts.map +1 -1
  40. package/build/middleware/fetch-utils.js +127 -14
  41. package/build/middleware/fetch-utils.js.map +1 -1
  42. package/build/middleware/fs-utils.d.ts +126 -0
  43. package/build/middleware/fs-utils.d.ts.map +1 -0
  44. package/build/middleware/fs-utils.js +229 -0
  45. package/build/middleware/fs-utils.js.map +1 -0
  46. package/build/middleware/logger.d.ts +16 -0
  47. package/build/middleware/logger.d.ts.map +1 -1
  48. package/build/middleware/logger.js +18 -0
  49. package/build/middleware/logger.js.map +1 -1
  50. package/build/middleware/output-processor.d.ts.map +1 -1
  51. package/build/middleware/output-processor.js +43 -8
  52. package/build/middleware/output-processor.js.map +1 -1
  53. package/build/middleware/parse-utils.d.ts +19 -0
  54. package/build/middleware/parse-utils.d.ts.map +1 -0
  55. package/build/middleware/parse-utils.js +30 -0
  56. package/build/middleware/parse-utils.js.map +1 -0
  57. package/build/middleware/png-utils.d.ts.map +1 -1
  58. package/build/middleware/png-utils.js +33 -11
  59. package/build/middleware/png-utils.js.map +1 -1
  60. package/build/middleware/result-handle.d.ts +76 -0
  61. package/build/middleware/result-handle.d.ts.map +1 -0
  62. package/build/middleware/result-handle.js +378 -0
  63. package/build/middleware/result-handle.js.map +1 -0
  64. package/build/middleware/sanitize.d.ts +26 -0
  65. package/build/middleware/sanitize.d.ts.map +1 -1
  66. package/build/middleware/sanitize.js +43 -2
  67. package/build/middleware/sanitize.js.map +1 -1
  68. package/build/middleware/security.d.ts.map +1 -1
  69. package/build/middleware/security.js +30 -5
  70. package/build/middleware/security.js.map +1 -1
  71. package/build/middleware/ui-dump.d.ts.map +1 -1
  72. package/build/middleware/ui-dump.js +30 -10
  73. package/build/middleware/ui-dump.js.map +1 -1
  74. package/build/server.d.ts.map +1 -1
  75. package/build/server.js +34 -5
  76. package/build/server.js.map +1 -1
  77. package/build/tool-context.d.ts.map +1 -1
  78. package/build/tool-context.js +2 -0
  79. package/build/tool-context.js.map +1 -1
  80. package/build/tools/accessibility.d.ts.map +1 -1
  81. package/build/tools/accessibility.js +39 -40
  82. package/build/tools/accessibility.js.map +1 -1
  83. package/build/tools/at-commands.d.ts.map +1 -1
  84. package/build/tools/at-commands.js +31 -6
  85. package/build/tools/at-commands.js.map +1 -1
  86. package/build/tools/baseband.d.ts.map +1 -1
  87. package/build/tools/baseband.js +18 -7
  88. package/build/tools/baseband.js.map +1 -1
  89. package/build/tools/build.d.ts.map +1 -1
  90. package/build/tools/build.js +30 -8
  91. package/build/tools/build.js.map +1 -1
  92. package/build/tools/ci.d.ts.map +1 -1
  93. package/build/tools/ci.js +16 -5
  94. package/build/tools/ci.js.map +1 -1
  95. package/build/tools/control.d.ts.map +1 -1
  96. package/build/tools/control.js +19 -11
  97. package/build/tools/control.js.map +1 -1
  98. package/build/tools/device-farm.d.ts.map +1 -1
  99. package/build/tools/device-farm.js +27 -3
  100. package/build/tools/device-farm.js.map +1 -1
  101. package/build/tools/device-profiles.d.ts.map +1 -1
  102. package/build/tools/device-profiles.js +70 -8
  103. package/build/tools/device-profiles.js.map +1 -1
  104. package/build/tools/device.d.ts.map +1 -1
  105. package/build/tools/device.js +4 -2
  106. package/build/tools/device.js.map +1 -1
  107. package/build/tools/diagnostics.d.ts.map +1 -1
  108. package/build/tools/diagnostics.js +17 -9
  109. package/build/tools/diagnostics.js.map +1 -1
  110. package/build/tools/emulator.d.ts.map +1 -1
  111. package/build/tools/emulator.js +12 -13
  112. package/build/tools/emulator.js.map +1 -1
  113. package/build/tools/files.d.ts +1 -1
  114. package/build/tools/files.d.ts.map +1 -1
  115. package/build/tools/files.js +98 -56
  116. package/build/tools/files.js.map +1 -1
  117. package/build/tools/firmware-analysis.d.ts.map +1 -1
  118. package/build/tools/firmware-analysis.js +35 -12
  119. package/build/tools/firmware-analysis.js.map +1 -1
  120. package/build/tools/forwarding.d.ts.map +1 -1
  121. package/build/tools/forwarding.js +248 -6
  122. package/build/tools/forwarding.js.map +1 -1
  123. package/build/tools/health.d.ts.map +1 -1
  124. package/build/tools/health.js +14 -3
  125. package/build/tools/health.js.map +1 -1
  126. package/build/tools/input-gestures.d.ts.map +1 -1
  127. package/build/tools/input-gestures.js +65 -13
  128. package/build/tools/input-gestures.js.map +1 -1
  129. package/build/tools/logcat-watch.d.ts.map +1 -1
  130. package/build/tools/logcat-watch.js +21 -14
  131. package/build/tools/logcat-watch.js.map +1 -1
  132. package/build/tools/logs.d.ts.map +1 -1
  133. package/build/tools/logs.js +8 -4
  134. package/build/tools/logs.js.map +1 -1
  135. package/build/tools/mirroring.d.ts.map +1 -1
  136. package/build/tools/mirroring.js +21 -19
  137. package/build/tools/mirroring.js.map +1 -1
  138. package/build/tools/multi-device.d.ts.map +1 -1
  139. package/build/tools/multi-device.js +10 -0
  140. package/build/tools/multi-device.js.map +1 -1
  141. package/build/tools/network-capture.d.ts.map +1 -1
  142. package/build/tools/network-capture.js +32 -8
  143. package/build/tools/network-capture.js.map +1 -1
  144. package/build/tools/network-discovery.d.ts.map +1 -1
  145. package/build/tools/network-discovery.js +11 -2
  146. package/build/tools/network-discovery.js.map +1 -1
  147. package/build/tools/ota-monitor.d.ts.map +1 -1
  148. package/build/tools/ota-monitor.js +26 -12
  149. package/build/tools/ota-monitor.js.map +1 -1
  150. package/build/tools/packages.d.ts.map +1 -1
  151. package/build/tools/packages.js +18 -11
  152. package/build/tools/packages.js.map +1 -1
  153. package/build/tools/plugins.d.ts +18 -0
  154. package/build/tools/plugins.d.ts.map +1 -1
  155. package/build/tools/plugins.js +47 -2
  156. package/build/tools/plugins.js.map +1 -1
  157. package/build/tools/prompts.d.ts.map +1 -1
  158. package/build/tools/prompts.js +2 -0
  159. package/build/tools/prompts.js.map +1 -1
  160. package/build/tools/qemu.d.ts +6 -3
  161. package/build/tools/qemu.d.ts.map +1 -1
  162. package/build/tools/qemu.js +28 -16
  163. package/build/tools/qemu.js.map +1 -1
  164. package/build/tools/registry.d.ts.map +1 -1
  165. package/build/tools/registry.js +76 -26
  166. package/build/tools/registry.js.map +1 -1
  167. package/build/tools/regression.d.ts +37 -0
  168. package/build/tools/regression.d.ts.map +1 -1
  169. package/build/tools/regression.js +63 -22
  170. package/build/tools/regression.js.map +1 -1
  171. package/build/tools/resources.d.ts.map +1 -1
  172. package/build/tools/resources.js +62 -15
  173. package/build/tools/resources.js.map +1 -1
  174. package/build/tools/result-handles.d.ts +37 -0
  175. package/build/tools/result-handles.d.ts.map +1 -0
  176. package/build/tools/result-handles.js +286 -0
  177. package/build/tools/result-handles.js.map +1 -0
  178. package/build/tools/ril-intercept.d.ts.map +1 -1
  179. package/build/tools/ril-intercept.js +15 -18
  180. package/build/tools/ril-intercept.js.map +1 -1
  181. package/build/tools/screen-record.d.ts.map +1 -1
  182. package/build/tools/screen-record.js +34 -5
  183. package/build/tools/screen-record.js.map +1 -1
  184. package/build/tools/screenshot-diff.d.ts.map +1 -1
  185. package/build/tools/screenshot-diff.js +29 -21
  186. package/build/tools/screenshot-diff.js.map +1 -1
  187. package/build/tools/selinux-audit.d.ts.map +1 -1
  188. package/build/tools/selinux-audit.js +7 -5
  189. package/build/tools/selinux-audit.js.map +1 -1
  190. package/build/tools/sensors.d.ts.map +1 -1
  191. package/build/tools/sensors.js +26 -4
  192. package/build/tools/sensors.js.map +1 -1
  193. package/build/tools/shell.d.ts.map +1 -1
  194. package/build/tools/shell.js +9 -0
  195. package/build/tools/shell.js.map +1 -1
  196. package/build/tools/snapshot.d.ts.map +1 -1
  197. package/build/tools/snapshot.js +38 -19
  198. package/build/tools/snapshot.js.map +1 -1
  199. package/build/tools/split-apk.d.ts.map +1 -1
  200. package/build/tools/split-apk.js +48 -24
  201. package/build/tools/split-apk.js.map +1 -1
  202. package/build/tools/test-gen.d.ts.map +1 -1
  203. package/build/tools/test-gen.js +29 -10
  204. package/build/tools/test-gen.js.map +1 -1
  205. package/build/tools/testing.d.ts.map +1 -1
  206. package/build/tools/testing.js +27 -9
  207. package/build/tools/testing.js.map +1 -1
  208. package/build/tools/thermal-power.d.ts +8 -1
  209. package/build/tools/thermal-power.d.ts.map +1 -1
  210. package/build/tools/thermal-power.js +35 -10
  211. package/build/tools/thermal-power.js.map +1 -1
  212. package/build/tools/ui.d.ts.map +1 -1
  213. package/build/tools/ui.js +20 -13
  214. package/build/tools/ui.js.map +1 -1
  215. package/build/tools/wireless-firmware.d.ts.map +1 -1
  216. package/build/tools/wireless-firmware.js +21 -7
  217. package/build/tools/wireless-firmware.js.map +1 -1
  218. package/build/tools/wireless.d.ts.map +1 -1
  219. package/build/tools/wireless.js +39 -3
  220. package/build/tools/wireless.js.map +1 -1
  221. package/build/tools/workflow-market.d.ts.map +1 -1
  222. package/build/tools/workflow-market.js +67 -13
  223. package/build/tools/workflow-market.js.map +1 -1
  224. package/build/tools/workflow.d.ts.map +1 -1
  225. package/build/tools/workflow.js +77 -8
  226. package/build/tools/workflow.js.map +1 -1
  227. package/build/ws-transport.d.ts.map +1 -1
  228. package/build/ws-transport.js +21 -3
  229. package/build/ws-transport.js.map +1 -1
  230. package/package.json +20 -8
@@ -0,0 +1,229 @@
1
+ // Copyright 2026 Jason <fullread@github>
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Filesystem Utilities - Shared helpers for safe on-disk state.
5
+ *
6
+ * Four primitives used across DeepADB tempDir-backed storage:
7
+ *
8
+ * 1. ensurePrivateDir(path) - Create a directory with restrictive
9
+ * permissions (0o700 on Unix; mode bits ignored on Windows). Use
10
+ * everywhere we create a subdirectory under tempDir. Defends against
11
+ * other-user reads on shared Unix hosts.
12
+ *
13
+ * 2. writeAtomicSync(path, data) - Write via a unique-per-call .tmp
14
+ * path then atomic rename. Defends against (a) mid-write crashes
15
+ * leaving corrupt data on disk, (b) concurrent readers seeing a
16
+ * partially-written file, (c) concurrent writers to the same path
17
+ * racing on .tmp filenames.
18
+ *
19
+ * 3. sanitizeFilenameComponent(name, maxLen?) - Replace any character
20
+ * outside [a-zA-Z0-9_-] with underscore. Optional maxLen truncates
21
+ * the result. Empty results are coerced to "_" so the caller never
22
+ * receives a zero-length component. Centralizes the pattern used
23
+ * across several tool modules for user-supplied identifier strings.
24
+ *
25
+ * 4. sanitizeFilenameComponentDotted(name, maxLen?) - Like (3) but
26
+ * ALSO allows "." in the output (for package names like
27
+ * "com.example.app"). Defangs pure-dot strings (".", "..", "...")
28
+ * by coercing them to "_", so a caller using the result directly
29
+ * as a path component cannot be steered into parent-directory
30
+ * traversal by an adversarial input.
31
+ *
32
+ * Note on existing directories: ensurePrivateDir only sets mode on
33
+ * directories it CREATES. Directories that already exist (from older
34
+ * DeepADB versions) keep their existing permissions. To remediate, the
35
+ * operator can chmod 700 the affected directories manually, or just
36
+ * delete them so they are recreated with the new mode.
37
+ */
38
+ import { mkdirSync, writeFileSync, renameSync, unlinkSync, readFileSync, openSync, fsyncSync, closeSync } from "fs";
39
+ import { resolve } from "path";
40
+ import { randomBytes } from "crypto";
41
+ /** Create directory with owner-only permissions. Idempotent. */
42
+ export function ensurePrivateDir(path) {
43
+ mkdirSync(path, { recursive: true, mode: 0o700 });
44
+ }
45
+ /** Atomic write: unique .tmp + rename. Throws on failure (caller handles). */
46
+ export function writeAtomicSync(path, data) {
47
+ // Unique per call: process + time + 4 random bytes. Defends against
48
+ // concurrent writers to the same target path (each picks its own .tmp).
49
+ const suffix = randomBytes(4).toString("hex");
50
+ const tmp = `${path}.${process.pid}.${Date.now()}.${suffix}.tmp`;
51
+ try {
52
+ writeFileSync(tmp, data);
53
+ // A4 fix: fsync the .tmp file's contents to durable storage BEFORE the
54
+ // atomic rename. Without this, a system crash between write and rename
55
+ // could leave the target file pointing at an empty or partial inode
56
+ // (the rename is atomic at the directory-entry level, but the data
57
+ // blocks of the renamed inode may not have been flushed yet on some
58
+ // filesystems). Cost: one extra syscall per write; negligible compared
59
+ // to the protection against crash-induced corruption of profiles,
60
+ // baselines, snapshots, and other persistent state. Falls through on
61
+ // platforms where fsync raises (rare in practice) — the rename still
62
+ // happens, matching pre-fix behavior.
63
+ try {
64
+ const fd = openSync(tmp, "r+");
65
+ try {
66
+ fsyncSync(fd);
67
+ }
68
+ finally {
69
+ closeSync(fd);
70
+ }
71
+ }
72
+ catch { /* fsync best-effort; do not block the rename */ }
73
+ renameSync(tmp, path);
74
+ }
75
+ catch (e) {
76
+ // Cleanup any partial state. If writeFileSync failed early, tmp may
77
+ // not exist; that is fine - unlinkSync just throws which we swallow.
78
+ try {
79
+ unlinkSync(tmp);
80
+ }
81
+ catch { /* ignore */ }
82
+ throw e;
83
+ }
84
+ }
85
+ /**
86
+ * Sanitize a string for use as a filename component. Replaces every
87
+ * character outside [a-zA-Z0-9_-] with "_".
88
+ *
89
+ * Safe against path traversal because `.`, `/`, and `\\` are all outside
90
+ * the allowlist (they become `_`). For cases where rejection is preferable
91
+ * to silent replacement, validate the raw input against /^[a-zA-Z0-9_-]+$/
92
+ * before calling tools.
93
+ *
94
+ * @param name Raw user-supplied identifier.
95
+ * @param maxLen Optional length cap applied after sanitization. Callers
96
+ * that build long paths (Windows MAX_PATH-sensitive) should
97
+ * always pass this.
98
+ * @returns Sanitized string, guaranteed non-empty (empty input collapses
99
+ * to "_" so callers never receive a zero-length component).
100
+ */
101
+ export function sanitizeFilenameComponent(name, maxLen) {
102
+ let s = name.replace(/[^a-zA-Z0-9_-]/g, "_");
103
+ // A6 fix: truncation can also produce empty (maxLen=0 edge case). Apply
104
+ // the empty-check AFTER truncation, matching the order used in
105
+ // sanitizeFilenameComponentDotted post-A7. Defense in depth: empty input
106
+ // would produce an empty component, which then concatenated with an
107
+ // extension yields a hidden file like ".json".
108
+ if (maxLen !== undefined && s.length > maxLen)
109
+ s = s.substring(0, maxLen);
110
+ // Collapse empty to "_" so the caller always gets a real component.
111
+ if (s.length === 0)
112
+ s = "_";
113
+ return s;
114
+ }
115
+ /**
116
+ * Like sanitizeFilenameComponent but ALSO allows "." in the output.
117
+ * Intended for Android-style package identifiers ("com.example.app") and
118
+ * similar dotted names where stripping dots would damage the identifier.
119
+ *
120
+ * Pure-dot strings (".", "..", "...", etc.) are coerced to "_" so a
121
+ * caller that uses the result directly as a path component cannot be
122
+ * steered into parent-directory traversal by an adversarial input.
123
+ *
124
+ * @param name Raw user-supplied dotted identifier (e.g., package name).
125
+ * @param maxLen Optional length cap applied after sanitization.
126
+ * @returns Sanitized string, guaranteed non-empty AND not a pure-dot
127
+ * traversal sequence.
128
+ */
129
+ /**
130
+ * A8 note: a LEADING dot is preserved (only character-class filtering
131
+ * applies — the dot is in the allowed set). On Unix, a result like
132
+ * `.hidden` becomes an actual hidden file in the result-handle store and
133
+ * other directory listings. This is intentional under the design (callers
134
+ * who want to allow package names like com.example.app need the dot), but
135
+ * means operators browsing the temp directory should pass `ls -A` not
136
+ * bare `ls` to see all entries. If a future caller wants the no-leading-
137
+ * dot behavior, it should sanitize the input before calling this helper.
138
+ */
139
+ export function sanitizeFilenameComponentDotted(name, maxLen) {
140
+ let s = name.replace(/[^a-zA-Z0-9_.-]/g, "_");
141
+ // A7 fix: defenses run AFTER truncation, not before. The previous order
142
+ // (defang → empty-check → truncate) had a gap: input "..a" with maxLen=2
143
+ // would pass the defang (not pure-dot), then truncate to "..", and a
144
+ // pure-dot traversal string would reach the caller. Same hazard if
145
+ // truncation produces an empty string. Apply both the pure-dot defang
146
+ // and the empty-check LAST so any reduction-by-truncation is caught.
147
+ if (maxLen !== undefined && s.length > maxLen)
148
+ s = s.substring(0, maxLen);
149
+ // Defang pure-dot strings: ".", "..", "..." would resolve to current-dir
150
+ // or parent-dir if passed as a path component without an extension.
151
+ // `join(dir, "..")` escapes dir entirely; this prevents that.
152
+ if (/^\.+$/.test(s))
153
+ s = "_";
154
+ // Empty-input defense (sanitizeFilenameComponent has the same).
155
+ if (s.length === 0)
156
+ s = "_";
157
+ return s;
158
+ }
159
+ /**
160
+ * Returns true iff `candidatePath`, after path resolution, is contained
161
+ * within `dir` — either equal to `dir` or strictly underneath it.
162
+ *
163
+ * Containment uses a path-separator boundary check, NOT a string prefix.
164
+ * Without the boundary, `candidatePath = /parent/tempDir_evil/x` would
165
+ * pass containment against `dir = /parent/tempDir` because the absolute
166
+ * path string-prefix-matches — even though the two are sibling directories.
167
+ *
168
+ * Both POSIX `/` and Windows `\\` separators are recognized so a candidate
169
+ * resolved on either platform is handled correctly.
170
+ *
171
+ * Usage:
172
+ * if (!isWithinDir(userOutputDir, ctx.config.tempDir)) {
173
+ * throw new Error("Output directory must be inside tempDir");
174
+ * }
175
+ *
176
+ * This is the centralized successor to the inline pattern that previously
177
+ * lived in tools/qemu.ts (verifyContainment, correctly using a separator
178
+ * boundary) and tools/split-apk.ts / tools/registry.ts (which used a bare
179
+ * string-prefix check and were vulnerable to the sibling-directory bypass).
180
+ * Lifting to shared middleware fixes the bypass at both call sites.
181
+ */
182
+ export function isWithinDir(candidatePath, dir) {
183
+ const resolvedCandidate = resolve(candidatePath);
184
+ const resolvedDir = resolve(dir);
185
+ if (resolvedCandidate === resolvedDir)
186
+ return true;
187
+ // A9 note: the two startsWith checks are intentionally redundant. After
188
+ // path.resolve(), the separator is platform-native — `/` on POSIX, `\\`
189
+ // on Windows — so one of the two checks ALWAYS misses on a given run.
190
+ // Keeping both means the code reads correctly regardless of platform,
191
+ // and the dead branch costs one extra string comparison per call. If a
192
+ // future contributor "cleans up" by removing one of the checks, they
193
+ // would break the code on the other platform. The dual check is a
194
+ // platform-portability defense, not a bug.
195
+ return resolvedCandidate.startsWith(resolvedDir + "/")
196
+ || resolvedCandidate.startsWith(resolvedDir + "\\");
197
+ }
198
+ /**
199
+ * Read and JSON-parse a file. On any failure (missing, unreadable, malformed),
200
+ * log a warning via the supplied logger and return null. Used by tools that
201
+ * walk a directory of JSON files (fingerprints, baselines, marketplace
202
+ * workflows, plugin metadata) and want to skip-but-surface corruption rather
203
+ * than abort the whole walk OR silently drop the entry.
204
+ *
205
+ * Closes audit findings AE8 / AO6 / AU6 / BJ7 / BN7 — the recurring silent
206
+ * `} catch { /* skip corrupt * / }` pattern. Surfaces corruption to the
207
+ * operator so a quietly-shrinking handle count doesn't go unnoticed.
208
+ *
209
+ * The logger is optional so this is safe to call from contexts without a
210
+ * Logger in scope (some loadFingerprints / listing functions don't take a
211
+ * ToolContext). When absent, the warning goes to stderr — non-silent.
212
+ *
213
+ * Returns null on any failure; caller should skip the entry.
214
+ */
215
+ export function tryReadJsonOrWarn(filePath, context, logger) {
216
+ try {
217
+ return JSON.parse(readFileSync(filePath, "utf-8"));
218
+ }
219
+ catch (err) {
220
+ const detail = err instanceof Error ? err.message : String(err);
221
+ const msg = `tryReadJsonOrWarn[${context}]: skipped ${filePath} — ${detail}`;
222
+ if (logger)
223
+ logger.warn(msg);
224
+ else
225
+ process.stderr.write(msg + "\n");
226
+ return null;
227
+ }
228
+ }
229
+ //# sourceMappingURL=fs-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-utils.js","sourceRoot":"","sources":["../../src/middleware/fs-utils.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,22 @@
3
3
  *
4
4
  * IMPORTANT: MCP stdio servers MUST NOT write to stdout.
5
5
  * All logging goes to stderr to avoid corrupting JSON-RPC messages.
6
+ *
7
+ * M1 THREAT-MODEL NOTE: This Logger does NOT redact sensitive content.
8
+ * Direct calls like `logger.info(\`token=${token}\`)` log credentials
9
+ * verbatim. Redaction lives at the SecurityMiddleware layer for audit-log
10
+ * entries (see middleware/security.ts `redactForLog`) and does NOT cover
11
+ * arbitrary Logger calls. Adding redaction at the Logger level was
12
+ * considered and rejected — too aggressive, it would hide useful debug
13
+ * info (e.g., truncated stack traces with hex offsets that look like
14
+ * tokens to a redactor). The divergent behavior IS the design:
15
+ *
16
+ * • SecurityMiddleware audit log → redacted (untrusted command strings)
17
+ * • Logger.info/warn/error → verbatim (callers are trusted code)
18
+ *
19
+ * Callers writing secret values into log strings are responsible for
20
+ * pre-redaction. The Logger trusts its own callers; it does not trust the
21
+ * commands flowing through SecurityMiddleware.
6
22
  */
7
23
  export type LogLevel = "debug" | "info" | "warn" | "error";
8
24
  export declare class Logger {
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAS3D,qBAAa,MAAM;IACjB,OAAO,CAAC,KAAK,CAAS;gBAEV,KAAK,GAAE,QAAiB;IAIpC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIhD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIhD,OAAO,CAAC,GAAG;CAOZ"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAS3D,qBAAa,MAAM;IACjB,OAAO,CAAC,KAAK,CAAS;gBAEV,KAAK,GAAE,QAAiB;IAIpC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIhD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIhD,OAAO,CAAC,GAAG;CAOZ"}
@@ -1,8 +1,26 @@
1
+ // Copyright 2026 Jason <fullread@github>
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Logger — Simple stderr-based logger for MCP servers.
3
5
  *
4
6
  * IMPORTANT: MCP stdio servers MUST NOT write to stdout.
5
7
  * All logging goes to stderr to avoid corrupting JSON-RPC messages.
8
+ *
9
+ * M1 THREAT-MODEL NOTE: This Logger does NOT redact sensitive content.
10
+ * Direct calls like `logger.info(\`token=${token}\`)` log credentials
11
+ * verbatim. Redaction lives at the SecurityMiddleware layer for audit-log
12
+ * entries (see middleware/security.ts `redactForLog`) and does NOT cover
13
+ * arbitrary Logger calls. Adding redaction at the Logger level was
14
+ * considered and rejected — too aggressive, it would hide useful debug
15
+ * info (e.g., truncated stack traces with hex offsets that look like
16
+ * tokens to a redactor). The divergent behavior IS the design:
17
+ *
18
+ * • SecurityMiddleware audit log → redacted (untrusted command strings)
19
+ * • Logger.info/warn/error → verbatim (callers are trusted code)
20
+ *
21
+ * Callers writing secret values into log strings are responsible for
22
+ * pre-redaction. The Logger trusts its own callers; it does not trust the
23
+ * commands flowing through SecurityMiddleware.
6
24
  */
7
25
  const LOG_LEVELS = {
8
26
  debug: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,UAAU,GAA6B;IAC3C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,MAAM,OAAO,MAAM;IACT,KAAK,CAAS;IAEtB,YAAY,QAAkB,MAAM;QAClC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;QACtC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;QACtC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;IAEO,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,GAAG,IAAe;QAC9D,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,SAAS,MAAM,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;YACzD,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,sCAAsC;AACtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,MAAM,UAAU,GAA6B;IAC3C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,MAAM,OAAO,MAAM;IACT,KAAK,CAAS;IAEtB,YAAY,QAAkB,MAAM;QAClC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;QACtC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAe;QACtC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAe;QACvC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;IAEO,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,GAAG,IAAe;QAC9D,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,SAAS,MAAM,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC;YACzD,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"output-processor.d.ts","sourceRoot":"","sources":["../../src/middleware/output-processor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,qBAAa,eAAe;IAC1B;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;IA2BvD;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAOlC;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;IAItD;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAO1C;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAWzD;;;;;;;OAOG;IACH,MAAM,CAAC,YAAY,CACjB,MAAM,EAAE,oBAAoB,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,EAChD,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,SAAgB,GACvB,MAAM;IAST;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAgCxC;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAmBxC;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAUzD"}
1
+ {"version":3,"file":"output-processor.d.ts","sourceRoot":"","sources":["../../src/middleware/output-processor.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAIH,qBAAa,eAAe;IAC1B;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;IAmCvD;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAOlC;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;IAItD;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAuB1C;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAWzD;;;;;;;OAOG;IACH,MAAM,CAAC,YAAY,CACjB,MAAM,EAAE,oBAAoB,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,EAChD,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,SAAgB,GACvB,MAAM;IAST;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAyCxC;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAmBxC;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAUzD"}
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Jason <fullread@github>
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * Output Processor — Truncation, formatting, structured parsing, and binary handling.
3
5
  *
@@ -15,10 +17,17 @@ export class OutputProcessor {
15
17
  const limit = maxSize ?? config.maxOutputSize;
16
18
  if (text.length <= limit)
17
19
  return text;
18
- // Scan backward from the limit to find a clean break point
20
+ // G1 fix: budget the truncation-suffix size into the limit BEFORE
21
+ // searching for a cut point. The suffix is appended after the cut,
22
+ // so without this reservation the returned string overshoots `limit`
23
+ // by ~75 chars (varies with the comma-formatted "remaining" count).
24
+ // Worst-case remaining count length: ~20 chars (for ~10^16 chars omitted).
25
+ const SUFFIX_RESERVE = 80;
26
+ const effectiveLimit = Math.max(0, limit - SUFFIX_RESERVE);
27
+ // Scan backward from the effective limit to find a clean break point
19
28
  // Look within the last 500 chars of the allowed range
20
- const searchStart = Math.max(0, limit - 500);
21
- const searchRegion = text.substring(searchStart, limit);
29
+ const searchStart = Math.max(0, effectiveLimit - 500);
30
+ const searchRegion = text.substring(searchStart, effectiveLimit);
22
31
  // Prefer section separators, then newlines
23
32
  let breakOffset = -1;
24
33
  const sectionBreak = searchRegion.lastIndexOf("\n\n");
@@ -31,7 +40,7 @@ export class OutputProcessor {
31
40
  breakOffset = searchStart + lineBreak;
32
41
  }
33
42
  }
34
- const cutPoint = breakOffset > 0 ? breakOffset : limit;
43
+ const cutPoint = breakOffset > 0 ? breakOffset : effectiveLimit;
35
44
  const truncated = text.substring(0, cutPoint);
36
45
  const remaining = text.length - cutPoint;
37
46
  return `${truncated}\n\n--- OUTPUT TRUNCATED (${remaining.toLocaleString()} characters omitted) ---`;
@@ -56,9 +65,26 @@ export class OutputProcessor {
56
65
  * Format a structured error response.
57
66
  */
58
67
  static formatError(error) {
68
+ // G2 fix: handle structured-object exceptions (some third-party libs
69
+ // throw plain objects rather than Error instances). Probe for a
70
+ // .message property first, then fall back to JSON for structured
71
+ // values, and finally String() for primitives. Avoids the
72
+ // unhelpful "Error: [object Object]" output.
59
73
  if (error instanceof Error) {
60
74
  return `Error: ${error.message}`;
61
75
  }
76
+ if (error && typeof error === "object") {
77
+ const msg = error.message;
78
+ if (typeof msg === "string" && msg.length > 0) {
79
+ return `Error: ${msg}`;
80
+ }
81
+ try {
82
+ return `Error: ${JSON.stringify(error)}`;
83
+ }
84
+ catch {
85
+ // Circular references or BigInt — fall through.
86
+ }
87
+ }
62
88
  return `Error: ${String(error)}`;
63
89
  }
64
90
  /**
@@ -118,10 +144,19 @@ export class OutputProcessor {
118
144
  const plugMap = { "0": "Unplugged", "1": "AC", "2": "USB", "4": "Wireless" };
119
145
  pairs.push(`Plugged: ${plugMap[plugged] ?? plugged}`);
120
146
  }
121
- if (temperature)
122
- pairs.push(`Temperature: ${(parseInt(temperature, 10) / 10).toFixed(1)}°C`);
123
- if (voltage)
124
- pairs.push(`Voltage: ${(parseInt(voltage, 10) / 1000).toFixed(3)}V`);
147
+ // G4 fix: skip non-numeric temperature/voltage values rather than emit
148
+ // "Temperature: NaN°C" or "Voltage: NaNV". Quirky ROMs or partial dumpsys
149
+ // output occasionally produce non-integer values here.
150
+ if (temperature) {
151
+ const tempNum = parseInt(temperature, 10);
152
+ if (Number.isFinite(tempNum))
153
+ pairs.push(`Temperature: ${(tempNum / 10).toFixed(1)}°C`);
154
+ }
155
+ if (voltage) {
156
+ const voltNum = parseInt(voltage, 10);
157
+ if (Number.isFinite(voltNum))
158
+ pairs.push(`Voltage: ${(voltNum / 1000).toFixed(3)}V`);
159
+ }
125
160
  return pairs.length > 0 ? pairs.join(" | ") : raw.trim();
126
161
  }
127
162
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"output-processor.js","sourceRoot":"","sources":["../../src/middleware/output-processor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"output-processor.js","sourceRoot":"","sources":["../../src/middleware/output-processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Parse Utilities — shared numeric/string parsing helpers.
3
+ *
4
+ * Q2 fix: previously \`parseIntSafe\` was duplicated in config.ts and
5
+ * result-handle.ts with slight signature differences. This module is the
6
+ * single source of truth. Both callers now import from here.
7
+ */
8
+ /**
9
+ * Parse a numeric environment variable with a defensive fallback.
10
+ *
11
+ * - Returns \`fallback\` when the value is undefined.
12
+ * - Returns \`fallback\` and logs a warning to stderr when the value is
13
+ * non-numeric or negative (operator typos).
14
+ * - The negative-rejection is intentional: every numeric config in DeepADB
15
+ * is a duration, count, or size — none of which has a legitimate
16
+ * negative interpretation.
17
+ */
18
+ export declare function parseIntSafe(value: string | undefined, fallback: number): number;
19
+ //# sourceMappingURL=parse-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-utils.d.ts","sourceRoot":"","sources":["../../src/middleware/parse-utils.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQhF"}
@@ -0,0 +1,30 @@
1
+ // Copyright 2026 Jason <fullread@github>
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Parse Utilities — shared numeric/string parsing helpers.
5
+ *
6
+ * Q2 fix: previously \`parseIntSafe\` was duplicated in config.ts and
7
+ * result-handle.ts with slight signature differences. This module is the
8
+ * single source of truth. Both callers now import from here.
9
+ */
10
+ /**
11
+ * Parse a numeric environment variable with a defensive fallback.
12
+ *
13
+ * - Returns \`fallback\` when the value is undefined.
14
+ * - Returns \`fallback\` and logs a warning to stderr when the value is
15
+ * non-numeric or negative (operator typos).
16
+ * - The negative-rejection is intentional: every numeric config in DeepADB
17
+ * is a duration, count, or size — none of which has a legitimate
18
+ * negative interpretation.
19
+ */
20
+ export function parseIntSafe(value, fallback) {
21
+ if (value === undefined)
22
+ return fallback;
23
+ const parsed = parseInt(value, 10);
24
+ if (isNaN(parsed) || parsed < 0) {
25
+ process.stderr.write(`[DeepADB] Invalid config value "${value}", using default: ${fallback}\n`);
26
+ return fallback;
27
+ }
28
+ return parsed;
29
+ }
30
+ //# sourceMappingURL=parse-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-utils.js","sourceRoot":"","sources":["../../src/middleware/parse-utils.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,sCAAsC;AACtC;;;;;;GAMG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,KAAyB,EAAE,QAAgB;IACtE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACnC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,KAAK,qBAAqB,QAAQ,IAAI,CAAC,CAAC;QAChG,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"png-utils.d.ts","sourceRoot":"","sources":["../../src/middleware/png-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyDH,iEAAiE;AACjE,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CASlE,CAAC;AAiBX;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAChE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAC9C,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACxC,SAAS,SAAI,GACZ,IAAI,CAmBN;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAChE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EACjC,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GACzC,IAAI,CA4CN;AAID,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAU,MAAM,CAAC;IACtB,MAAM,EAAS,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAS,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAkFhE;AAID;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CA0B3F"}
1
+ {"version":3,"file":"png-utils.d.ts","sourceRoot":"","sources":["../../src/middleware/png-utils.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAyDH,iEAAiE;AACjE,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CASlE,CAAC;AA4BX;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAChE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAC9C,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACxC,SAAS,SAAI,GACZ,IAAI,CAmBN;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAChE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EACjC,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GACzC,IAAI,CA4CN;AAID,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAU,MAAM,CAAC;IACtB,MAAM,EAAS,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAS,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAkFhE;AAID;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CAqC3F"}
@@ -1,3 +1,5 @@
1
+ // Copyright 2026 Jason <fullread@github>
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  /**
2
4
  * PNG Utilities — Zero-dependency PNG decode and encode for screenshot analysis and annotation.
3
5
  *
@@ -66,14 +68,26 @@ export const ELEMENT_COLORS = [
66
68
  [160, 50, 220], // purple
67
69
  ];
68
70
  // ── Pixel operations ───────────────────────────────────────────────────
69
- /** Write one pixel into a packed row-major RGB/RGBA pixel buffer. No-op if x is out of range. */
70
- function setPixel(pixels, imgWidth, bpp, x, y, r, g, b) {
71
+ /** Write one pixel into a packed row-major RGB/RGBA pixel buffer. No-op if
72
+ * x or y is out of range. Defense in depth: callers (drawRect, drawLabel)
73
+ * already pre-check bounds, but having the guard here too means a future
74
+ * caller that forgets the check cannot silently corrupt a different row
75
+ * — without the y-bound check, a y >= imgHeight produces a positive offset
76
+ * that still falls inside the buffer (it just points to a wrong row, or
77
+ * past the end where Node's Buffer silently no-ops). */
78
+ function setPixel(pixels, imgWidth, imgHeight, bpp, x, y, r, g, b) {
71
79
  if (x < 0 || x >= imgWidth)
72
80
  return;
81
+ if (y < 0 || y >= imgHeight)
82
+ return;
83
+ // I1 fix: clamp R/G/B to [0, 255]. Previously out-of-range values were
84
+ // silently truncated by Buffer coercion — pixels[off]=300 wraps to 44
85
+ // (300 & 0xFF). Clamping surfaces the caller bug as a visible saturated
86
+ // pixel instead of producing arbitrary wrong colors.
73
87
  const off = (y * imgWidth + x) * bpp;
74
- pixels[off] = r;
75
- pixels[off + 1] = g;
76
- pixels[off + 2] = b;
88
+ pixels[off] = Math.max(0, Math.min(255, r | 0));
89
+ pixels[off + 1] = Math.max(0, Math.min(255, g | 0));
90
+ pixels[off + 2] = Math.max(0, Math.min(255, b | 0));
77
91
  if (bpp === 4)
78
92
  pixels[off + 3] = 255;
79
93
  }
@@ -92,12 +106,12 @@ export function drawRect(pixels, imgWidth, imgHeight, bpp, x1, y1, x2, y2, color
92
106
  if (top > bot || left > right)
93
107
  break;
94
108
  for (let x = left; x <= right; x++) {
95
- setPixel(pixels, imgWidth, bpp, x, top, r, g, b);
96
- setPixel(pixels, imgWidth, bpp, x, bot, r, g, b);
109
+ setPixel(pixels, imgWidth, imgHeight, bpp, x, top, r, g, b);
110
+ setPixel(pixels, imgWidth, imgHeight, bpp, x, bot, r, g, b);
97
111
  }
98
112
  for (let y = top + 1; y < bot; y++) {
99
- setPixel(pixels, imgWidth, bpp, left, y, r, g, b);
100
- setPixel(pixels, imgWidth, bpp, right, y, r, g, b);
113
+ setPixel(pixels, imgWidth, imgHeight, bpp, left, y, r, g, b);
114
+ setPixel(pixels, imgWidth, imgHeight, bpp, right, y, r, g, b);
101
115
  }
102
116
  }
103
117
  }
@@ -127,7 +141,7 @@ export function drawLabel(pixels, imgWidth, imgHeight, bpp, x, y, num, bgColor)
127
141
  // Fill background rectangle
128
142
  for (let dy = 0; dy < labelH && ly + dy < imgHeight; dy++) {
129
143
  for (let dx = 0; dx < labelW && lx + dx < imgWidth; dx++) {
130
- setPixel(pixels, imgWidth, bpp, lx + dx, ly + dy, bgR, bgG, bgB);
144
+ setPixel(pixels, imgWidth, imgHeight, bpp, lx + dx, ly + dy, bgR, bgG, bgB);
131
145
  }
132
146
  }
133
147
  // Render each digit glyph
@@ -141,7 +155,7 @@ export function drawLabel(pixels, imgWidth, imgHeight, bpp, x, y, num, bgColor)
141
155
  const px = lx + PAD + ci * CHAR_W + col;
142
156
  const py = ly + PAD + row;
143
157
  if (px < imgWidth && py < imgHeight) {
144
- setPixel(pixels, imgWidth, bpp, px, py, fr, fg, fb);
158
+ setPixel(pixels, imgWidth, imgHeight, bpp, px, py, fr, fg, fb);
145
159
  }
146
160
  }
147
161
  }
@@ -248,6 +262,14 @@ export function decodePngPixels(pngPath) {
248
262
  * bpp must be 3 (RGB) or 4 (RGBA) to match the input data.
249
263
  */
250
264
  export function encodePng(width, height, pixels, bpp) {
265
+ // I3 fix: pixel buffer size precondition. A mismatched buffer (e.g.,
266
+ // wrong bpp passed) previously produced a corrupt PNG silently because
267
+ // pixels.copy(..., over-read) is a no-op. Surface the mismatch as an
268
+ // error so the caller bug is caught at the boundary.
269
+ const expectedSize = width * height * bpp;
270
+ if (pixels.length < expectedSize) {
271
+ throw new Error(`encodePng: pixel buffer too small. Got ${pixels.length} bytes, expected at least ${expectedSize} (${width}x${height} at ${bpp} bpp).`);
272
+ }
251
273
  const SIG = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
252
274
  const ihdr = Buffer.allocUnsafe(13);
253
275
  ihdr.writeUInt32BE(width, 0);