supercov 4.24.1 → 4.24.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supercov",
|
|
3
|
-
"version": "4.24.
|
|
3
|
+
"version": "4.24.3",
|
|
4
4
|
"description": "Coverage, security and code quality for coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"coverage",
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
"check:tooling": "npm run test:release-tooling && npm run test:runtime && npm run test:action && npm run test:rust-assets && npm run test:agent-commands && npm run test:assertions && npm run test:sweep-target && npm run test:skip-guard && npm run test:windows-tls && node scripts/package-preflight.mjs && node scripts/verify-binstall-metadata.mjs --offline",
|
|
102
102
|
"release:check": "cargo clean && node scripts/sweep-target.mjs && npm run check && npm run test:engine && npm run test:go && npm run test:jvm && npm run test:launcher && npm run test:fixture && npm run test:watchdog && npm run test:engine-contract && npm run test:agent && npm run test:child-attribution && npm run test:host-loader && npm run test:python && npm run test:ruby-coverage && npm run test:rust-public-cargo && npm run test:packed-npx && npm run test:clang-mcdc && npm run benchmark:check",
|
|
103
103
|
"release:bump": "node scripts/bump-version.mjs",
|
|
104
|
+
"release:verify": "node scripts/verify-release.mjs",
|
|
104
105
|
"sweep": "node scripts/sweep-target.mjs",
|
|
105
106
|
"oracle:rust": "cargo build --release -p supercov && node scripts/rust-coverage-oracle.mjs",
|
|
106
107
|
"prepack": "node scripts/package-preflight.mjs",
|
|
@@ -122,14 +123,14 @@
|
|
|
122
123
|
"test:windows-tls": "node scripts/windows-tls-test.mjs"
|
|
123
124
|
},
|
|
124
125
|
"optionalDependencies": {
|
|
125
|
-
"@supercov/cli-darwin-arm64": "4.24.
|
|
126
|
-
"@supercov/cli-darwin-x64": "4.24.
|
|
127
|
-
"@supercov/cli-linux-arm64-gnu": "4.24.
|
|
128
|
-
"@supercov/cli-linux-arm64-musl": "4.24.
|
|
129
|
-
"@supercov/cli-linux-x64-gnu": "4.24.
|
|
130
|
-
"@supercov/cli-linux-x64-musl": "4.24.
|
|
131
|
-
"@supercov/cli-win32-arm64": "4.24.
|
|
132
|
-
"@supercov/cli-win32-x64": "4.24.
|
|
126
|
+
"@supercov/cli-darwin-arm64": "4.24.3",
|
|
127
|
+
"@supercov/cli-darwin-x64": "4.24.3",
|
|
128
|
+
"@supercov/cli-linux-arm64-gnu": "4.24.3",
|
|
129
|
+
"@supercov/cli-linux-arm64-musl": "4.24.3",
|
|
130
|
+
"@supercov/cli-linux-x64-gnu": "4.24.3",
|
|
131
|
+
"@supercov/cli-linux-x64-musl": "4.24.3",
|
|
132
|
+
"@supercov/cli-win32-arm64": "4.24.3",
|
|
133
|
+
"@supercov/cli-win32-x64": "4.24.3"
|
|
133
134
|
},
|
|
134
135
|
"peerDependencies": {
|
|
135
136
|
"@playwright/test": ">=1.55.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appendFileSync, existsSync, mkdirSync, openSync, closeSync, fsyncSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
1
|
+
import { appendFileSync, chmodSync, existsSync, mkdirSync, openSync, closeSync, fsyncSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { Buffer } from "node:buffer";
|
|
@@ -18,6 +18,22 @@ function makeDirectory(directory) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// And a file made there is readable by every user, whatever the process's
|
|
22
|
+
// mask: a container's root wrote evidence only it could read, and the run,
|
|
23
|
+
// as another user, recorded nothing ("Permission denied", 4.24.1).
|
|
24
|
+
const inContainer = () => process.env.SUPERCOV_CONTAINER === "1";
|
|
25
|
+
const fileMode = () => (inContainer() ? 0o666 : 0o600);
|
|
26
|
+
function openToAll(path) {
|
|
27
|
+
if (!inContainer())
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
chmodSync(path, 0o666);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Left as the mask made it: the run says what it could not read.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
21
37
|
function fsyncDirectory(path) {
|
|
22
38
|
try {
|
|
23
39
|
const directory = openSync(path, "r");
|
|
@@ -38,7 +54,8 @@ export function atomicWriteFileSync(path, data, options) {
|
|
|
38
54
|
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
39
55
|
let descriptor;
|
|
40
56
|
try {
|
|
41
|
-
descriptor = openSync(temporary, "wx",
|
|
57
|
+
descriptor = openSync(temporary, "wx", fileMode());
|
|
58
|
+
openToAll(temporary);
|
|
42
59
|
writeFileSync(descriptor, data, options);
|
|
43
60
|
fsyncSync(descriptor);
|
|
44
61
|
closeSync(descriptor);
|
|
@@ -59,7 +76,7 @@ export function atomicWriteFileSync(path, data, options) {
|
|
|
59
76
|
export function appendJsonLineDurableSync(path, data) {
|
|
60
77
|
makeDirectory(dirname(path));
|
|
61
78
|
const existed = existsSync(path);
|
|
62
|
-
const descriptor = openSync(path, "a",
|
|
79
|
+
const descriptor = openSync(path, "a", fileMode());
|
|
63
80
|
try {
|
|
64
81
|
writeFileSync(descriptor, data.endsWith("\n") ? data : `${data}\n`);
|
|
65
82
|
fsyncSync(descriptor);
|
|
@@ -67,13 +84,18 @@ export function appendJsonLineDurableSync(path, data) {
|
|
|
67
84
|
finally {
|
|
68
85
|
closeSync(descriptor);
|
|
69
86
|
}
|
|
70
|
-
if (!existed)
|
|
87
|
+
if (!existed) {
|
|
88
|
+
openToAll(path);
|
|
71
89
|
fsyncDirectory(dirname(path));
|
|
90
|
+
}
|
|
72
91
|
}
|
|
73
92
|
/** Append a complete local record; process exit closes it before publication. */
|
|
74
93
|
export function appendJsonLineSync(path, data) {
|
|
75
94
|
makeDirectory(dirname(path));
|
|
76
|
-
|
|
95
|
+
const opening = inContainer() && !existsSync(path);
|
|
96
|
+
appendFileSync(path, data.endsWith("\n") ? data : `${data}\n`, { mode: fileMode() });
|
|
97
|
+
if (opening)
|
|
98
|
+
openToAll(path);
|
|
77
99
|
}
|
|
78
100
|
/** Atomically publish a fully prepared file or directory and persist its entry. */
|
|
79
101
|
export function atomicRenameSync(source, destination) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// CommonJS on purpose: Jest's module system loads setup files without the
|
|
11
11
|
// ESM flag, and the runtime it needs is already on the sandbox global.
|
|
12
12
|
const { createHash, randomUUID } = require("node:crypto");
|
|
13
|
-
const { appendFileSync, closeSync, fsyncSync, mkdirSync, openSync, statSync, writeFileSync } = require("node:fs");
|
|
13
|
+
const { appendFileSync, chmodSync, closeSync, fsyncSync, mkdirSync, openSync, statSync, writeFileSync } = require("node:fs");
|
|
14
14
|
const { relative, resolve, sep } = require("node:path");
|
|
15
15
|
const { Buffer } = require("node:buffer");
|
|
16
16
|
|
|
@@ -115,8 +115,13 @@ function writeEvidence(payload) {
|
|
|
115
115
|
const writer = (process.env.SUPERCOV_EXECUTION_LOG_SHARD ?? `pid-${process.pid}`).replace(/[^A-Za-z0-9_-]/g, "_");
|
|
116
116
|
journal = { path: resolve(directory, `jest-worker-${writer}-${randomUUID()}.mcdc.jsonl`), size: 0 };
|
|
117
117
|
}
|
|
118
|
+
// In a container its root writes the journal, and the run, as another
|
|
119
|
+
// user, reads it: opened to all there, as atomic.mjs opens its files.
|
|
120
|
+
const container = process.env.SUPERCOV_CONTAINER === "1";
|
|
121
|
+
const mode = container ? 0o666 : 0o600;
|
|
122
|
+
const opening = container && journal.size === 0;
|
|
118
123
|
if (process.env.SUPERCOV_DURABLE_EVIDENCE_EACH_TEST === "1") {
|
|
119
|
-
const descriptor = openSync(journal.path, "a",
|
|
124
|
+
const descriptor = openSync(journal.path, "a", mode);
|
|
120
125
|
try {
|
|
121
126
|
writeFileSync(descriptor, line);
|
|
122
127
|
fsyncSync(descriptor);
|
|
@@ -126,7 +131,15 @@ function writeEvidence(payload) {
|
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
else {
|
|
129
|
-
appendFileSync(journal.path, line, { mode
|
|
134
|
+
appendFileSync(journal.path, line, { mode });
|
|
135
|
+
}
|
|
136
|
+
if (opening) {
|
|
137
|
+
try {
|
|
138
|
+
chmodSync(journal.path, 0o666);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Left as the mask made it.
|
|
142
|
+
}
|
|
130
143
|
}
|
|
131
144
|
journal.size += Buffer.byteLength(line);
|
|
132
145
|
}
|
|
@@ -729,6 +729,39 @@ function injectChildEnvironment(method, args) {
|
|
|
729
729
|
});
|
|
730
730
|
return next;
|
|
731
731
|
}
|
|
732
|
+
// A signal a crash raises, never the SIGTERM or SIGKILL a suite's teardown
|
|
733
|
+
// sends. A child it ends says nothing: on 2026-11-06 a backend that died so
|
|
734
|
+
// right after it listened read as one that never became ready, and a
|
|
735
|
+
// Playwright worker as one that "exited unexpectedly".
|
|
736
|
+
const CRASH_SIGNALS = new Set(["SIGSEGV", "SIGBUS", "SIGILL", "SIGFPE", "SIGABRT", "SIGTRAP", "SIGSYS"]);
|
|
737
|
+
function noteCrash(signal, child, command, started) {
|
|
738
|
+
if (!CRASH_SIGNALS.has(signal))
|
|
739
|
+
return;
|
|
740
|
+
record({
|
|
741
|
+
event: "child-exit",
|
|
742
|
+
...(typeof child === "number" ? { child } : {}),
|
|
743
|
+
signal,
|
|
744
|
+
command: safeArgument(String(command ?? "")),
|
|
745
|
+
ranMs: Date.now() - started,
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
function watchChild(method, args, run) {
|
|
749
|
+
const started = Date.now();
|
|
750
|
+
let launched;
|
|
751
|
+
try {
|
|
752
|
+
launched = run();
|
|
753
|
+
}
|
|
754
|
+
catch (error) {
|
|
755
|
+
// execSync and execFileSync throw for a child a signal ended.
|
|
756
|
+
noteCrash(error?.signal, error?.pid, args[0], started);
|
|
757
|
+
throw error;
|
|
758
|
+
}
|
|
759
|
+
if (method === "spawnSync")
|
|
760
|
+
noteCrash(launched?.signal, launched?.pid, args[0], started);
|
|
761
|
+
else if (typeof launched?.once === "function")
|
|
762
|
+
launched.once("exit", (_code, signal) => noteCrash(signal, launched.pid, args[0], started));
|
|
763
|
+
return launched;
|
|
764
|
+
}
|
|
732
765
|
function patchChildProcesses() {
|
|
733
766
|
const methods = [
|
|
734
767
|
"spawn",
|
|
@@ -745,7 +778,7 @@ function patchChildProcesses() {
|
|
|
745
778
|
configurable: true,
|
|
746
779
|
enumerable: true,
|
|
747
780
|
writable: true,
|
|
748
|
-
value: keepPromisifiedShape(original, (...args) => Reflect.apply(original, childProcess, injectChildEnvironment(method, args))),
|
|
781
|
+
value: keepPromisifiedShape(original, (...args) => watchChild(method, args, () => Reflect.apply(original, childProcess, injectChildEnvironment(method, args)))),
|
|
749
782
|
});
|
|
750
783
|
}
|
|
751
784
|
syncBuiltinESMExports();
|
|
@@ -13,6 +13,27 @@ import { fileURLToPath } from "node:url";
|
|
|
13
13
|
import { installLaunchSupervisor, wrapImportedCapability } from "./launchSupervisor.mjs";
|
|
14
14
|
import { __supercovBindCapabilityWrapper } from "./capability.mjs";
|
|
15
15
|
installLaunchSupervisor();
|
|
16
|
+
// Playwright 1.59 writes each file it compiles into its cache in place and
|
|
17
|
+
// reads it back unchecked: a process that read one while another wrote it
|
|
18
|
+
// ran an empty module, and a config of no `default` failed at "Cannot add
|
|
19
|
+
// property metadata, object is not extensible". The cache a run gives it
|
|
20
|
+
// starts empty, so every run compiled; there each file is written whole, or
|
|
21
|
+
// is not there yet and is compiled again. A file written beside the cache's
|
|
22
|
+
// folders, not in one, is never taken for an old entry and removed.
|
|
23
|
+
const transformCache = process.env.PWTEST_CACHE_DIR;
|
|
24
|
+
if (transformCache) {
|
|
25
|
+
const cacheFolder = resolve(transformCache) + sep;
|
|
26
|
+
const writeFileSync = fs.writeFileSync;
|
|
27
|
+
let written = 0;
|
|
28
|
+
fs.writeFileSync = function (file, ...rest) {
|
|
29
|
+
if (typeof file !== "string" || !resolve(file).startsWith(cacheFolder))
|
|
30
|
+
return writeFileSync.call(this, file, ...rest);
|
|
31
|
+
const partial = resolve(transformCache, `.writing-${process.pid}-${written++}`);
|
|
32
|
+
writeFileSync.call(this, partial, ...rest);
|
|
33
|
+
fs.renameSync(partial, file);
|
|
34
|
+
};
|
|
35
|
+
syncBuiltinESMExports();
|
|
36
|
+
}
|
|
16
37
|
// Instrumented sources import the browser-safe capability seam; bind the
|
|
17
38
|
// real supervisor implementation before any user module evaluates.
|
|
18
39
|
__supercovBindCapabilityWrapper(wrapImportedCapability);
|
|
@@ -651,7 +651,8 @@ function markContainerWriting(fs) {
|
|
|
651
651
|
state.containerMarked = true;
|
|
652
652
|
try {
|
|
653
653
|
fs.mkdirSync(mark.slice(0, mark.lastIndexOf("/")), { recursive: true });
|
|
654
|
-
fs.writeFileSync(mark, "");
|
|
654
|
+
fs.writeFileSync(mark, "", { mode: 0o666 });
|
|
655
|
+
fs.chmodSync(mark, 0o666);
|
|
655
656
|
} catch (e) {
|
|
656
657
|
}
|
|
657
658
|
}
|