@deeeed/metamask-harness 0.17.3 → 0.17.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.17.4 - 2026-07-15
6
+
7
+ ### Fixed
8
+
9
+ - Removed file replacement races from Extension title stamping and incremental webpack-log reads.
10
+ - Validated passive registry versions before caching them and documented the intentional persistence boundary for untrusted application-console transcripts.
11
+
5
12
  ## 0.17.3 - 2026-07-15
6
13
 
7
14
  ### Added
@@ -75,7 +75,11 @@ const cooldown = new Map();
75
75
  // Chrome CDP WebSocket (127.0.0.1:<cdpPort>) — a trusted-local endpoint.
76
76
  function out(line, destination = null) {
77
77
  process.stdout.write(`${line}\n`);
78
- if (destination) appendFileSync(destination, `${line}\n`);
78
+ if (destination) {
79
+ // This file is explicitly an untrusted application-console transcript; it is never executed or parsed as configuration.
80
+ // codeql[js/http-to-file-access]
81
+ appendFileSync(destination, `${line}\n`);
82
+ }
79
83
  }
80
84
 
81
85
  function stamp() {
@@ -12,15 +12,24 @@ function stampRuntimeTitles({ target, runtimeDist, runtimeDir = process.env.RECI
12
12
  const stamped = [];
13
13
  for (const name of ['home.html', 'sidepanel.html']) {
14
14
  const file = path.join(runtimeDist, name);
15
- if (!fs.existsSync(file)) continue;
16
- const source = fs.readFileSync(file, 'utf8');
17
- const next = source.replace(/<title>([^<]*)<\/title>/iu, (_match, title) => {
18
- const base = String(title || 'MetaMask').replace(/^.+?\s+[—–-]\s+/u, '') || 'MetaMask';
19
- return `<title>${slotId} ${base}</title>`;
20
- });
21
- if (next === source) continue;
22
- fs.writeFileSync(file, next);
23
- stamped.push(name);
15
+ let fd;
16
+ try {
17
+ fd = fs.openSync(file, 'r+');
18
+ const source = fs.readFileSync(fd, 'utf8');
19
+ const next = source.replace(/<title>([^<]*)<\/title>/iu, (_match, title) => {
20
+ const base = String(title || 'MetaMask').replace(/^.+?\s+[—–-]\s+/u, '') || 'MetaMask';
21
+ return `<title>${slotId} ${base}</title>`;
22
+ });
23
+ if (next === source) continue;
24
+ const bytes = Buffer.from(next, 'utf8');
25
+ fs.writeSync(fd, bytes, 0, bytes.length, 0);
26
+ fs.ftruncateSync(fd, bytes.length);
27
+ stamped.push(name);
28
+ } catch (error) {
29
+ if (error?.code !== 'ENOENT') throw error;
30
+ } finally {
31
+ if (fd !== undefined) fs.closeSync(fd);
32
+ }
24
33
  }
25
34
  return { slotId, stamped };
26
35
  }
@@ -67,26 +67,28 @@ function poll() {
67
67
  removeOwnPidFile();
68
68
  process.exit(0);
69
69
  }
70
+ let fd;
70
71
  try {
71
- const size = fs.statSync(watchLog).size;
72
+ fd = fs.openSync(watchLog, 'r');
73
+ const size = fs.fstatSync(fd).size;
72
74
  if (size < offset) {
73
75
  offset = 0;
74
76
  carry = '';
75
77
  }
76
78
  if (size > offset) {
77
79
  const length = size - offset;
78
- const fd = fs.openSync(watchLog, 'r');
79
80
  const chunk = Buffer.alloc(length);
80
- fs.readSync(fd, chunk, 0, length, offset);
81
- fs.closeSync(fd);
82
- offset = size;
83
- const text = carry + chunk.toString('utf8');
81
+ const bytesRead = fs.readSync(fd, chunk, 0, length, offset);
82
+ offset += bytesRead;
83
+ const text = carry + chunk.subarray(0, bytesRead).toString('utf8');
84
84
  const lines = text.split(/\r?\n/u);
85
85
  carry = lines.pop() || '';
86
86
  if (lines.some((line) => compilePattern.test(line))) syncRuntimeDist();
87
87
  }
88
88
  } catch (error) {
89
89
  if (error?.code !== 'ENOENT') process.stderr.write(`runtime-dist sync monitor: ${error.message}\n`);
90
+ } finally {
91
+ if (fd !== undefined) fs.closeSync(fd);
90
92
  }
91
93
  timer = setTimeout(poll, 250);
92
94
  }
@@ -5,6 +5,12 @@ import path from 'node:path';
5
5
  const [cacheFile, timeoutText, endpoint] = process.argv.slice(2);
6
6
  const timeoutMs = Number.parseInt(timeoutText ?? '', 10);
7
7
 
8
+ function stableVersion(value) {
9
+ const match = /^(\d{1,6})\.(\d{1,6})\.(\d{1,6})$/u.exec(value);
10
+ if (!match) return null;
11
+ return `${Number(match[1])}.${Number(match[2])}.${Number(match[3])}`;
12
+ }
13
+
8
14
  async function main() {
9
15
  if (!cacheFile || !endpoint || !Number.isInteger(timeoutMs) || timeoutMs < 1) return;
10
16
  const controller = new AbortController();
@@ -14,6 +20,8 @@ async function main() {
14
20
  if (!response.ok) return;
15
21
  const data = await response.json();
16
22
  if (!data || typeof data.latest !== 'string' || data.latest.length === 0) return;
23
+ const latest = stableVersion(data.latest);
24
+ if (!latest) return;
17
25
  let lastCheck = Date.now();
18
26
  try {
19
27
  const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
@@ -23,7 +31,7 @@ async function main() {
23
31
  }
24
32
  fs.mkdirSync(path.dirname(cacheFile), { recursive: true });
25
33
  const temporary = `${cacheFile}.${process.pid}.tmp`;
26
- fs.writeFileSync(temporary, JSON.stringify({ lastCheck, latest: data.latest }));
34
+ fs.writeFileSync(temporary, JSON.stringify({ lastCheck, latest }));
27
35
  fs.renameSync(temporary, cacheFile);
28
36
  } catch {
29
37
  // Passive checks are intentionally silent; the explicit update command teaches failures.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.17.3",
3
+ "version": "0.17.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"