Files
maka/scripts/check-allow-scripts.test.mjs
小七 eb1c68a785 chore: refresh the install-scripts allowlist against the lockfile (#5744)
npm's install-scripts approval gate flags seven packages on a fresh
clone: two allowlist entries trail the lockfile (esbuild 0.27.7 vs the
pinned 0.28.2, @jackwener/opencli 1.8.4 vs 1.8.7), and five packages
with install scripts were never allowlisted at all (electron-winstaller,
protobufjs, tree-sitter-javascript, @astryxdesign/cli,
@astryxdesign/core). Every entry is now the exact version pinned in
package-lock.json, verified against node_modules, and a fresh install
completes with no unreviewed-script warnings.

Generated-by: GLM-5.3-Flash (ZCode)
chore: deny the four nonessential install scripts

Review follow-up: the @astryxdesign/cli and @astryxdesign/core
postinstalls only print setup nudges, protobufjs only checks its version
notation, and @jackwener/opencli skips its shell-completion postinstall
on this repository's installs and in CI — none produces an artifact
Maka needs. Record an explicit deny (which also clears npm's
unreviewed-script warning without executing the scripts) and keep
approval only for the scripts installation genuinely requires
(esbuild's binary install, tree-sitter-javascript's native build,
electron-winstaller's arch selection, node-pty's native build).

Generated-by: GLM-5.3-Flash (ZCode)
chore: allowlist fsevents@2.3.3 install script

fsevents@2.3.3 is a darwin-only optional dev dependency with
hasInstallScript: true in the lockfile; a fresh install on macOS trips the
approval gate exactly like the eight entries this PR already refreshed.
The allowlist was scanned on Linux, where the os: ["darwin"] guard keeps
the package out of node_modules - that is how it was missed.

Generated-by: GLM-5.3-Flash (ZCode)
chore(ci): guard allowScripts against lockfile drift

npm's install-script approval gate keys approvals on exact name@version,
so a routine bump silently stales the map and a new optional script-
carrying dependency (fsevents) escapes it entirely. The new
check-allow-scripts script fails on any script-carrying lockfile entry
the map does not key, any map key trailing the lockfile, and any
non-boolean decision; the dependency-audit workflow runs it beside the
shipped-closure audit so the drift cannot land.

Generated-by: GLM-5.3-Flash (ZCode)
Merge remote-tracking branch 'upstream/main' into fix/allow-scripts-versions
chore(ci): retrigger after a flaky code-mode run

The affected-test lane failed on a single timing-sensitive assertion in
code-mode.test.js ("excludes host waiting from the execution budget and
preserves dependent Promise.race progress", false !== true). The same
test file passes three consecutive local runs (33/33 each) on this
exact tree; nothing in this PR touches the runtime. Tree-identical
retrigger.

Generated-by: GLM-5.3-Flash (ZCode)
chore(ci): retrigger the windows packaging lane again

The Release Windows package job crashed twice at the same point: the
electron-builder icons@1.2.3 icon-tool child process died with
0xC0000005 on the Windows runner (the ico output had already been
written). main's own package lane passes on the same sources, so this
is runner-side, not this PR. Third attempt; if it crashes at the same
point again I will file an issue with the three logs.

Generated-by: GLM-5.3-Flash (ZCode)
Merge upstream/main (8648258d0)

Brings in the dependency-refresh train through #5839/#5860. The single
conflict was the root allowScripts block: upstream mechanically carried
"@jackwener/opencli@1.8.8": true forward from the dependabot bump, while
this branch holds the reviewed deny (opencli's skipped shell-completion
postinstall produces no artifact Maka needs). Keep the deny decisions and
re-key the three bumped versions to the new lockfile:
@jackwener/opencli 1.8.7 -> 1.8.8, @astryxdesign/cli and
@astryxdesign/core 0.6.2 -> 0.6.3. The other six entries already match.
scripts/check-allow-scripts.mjs passes against the merged lockfile
(9/9 install-script packages covered) and its suite stays green.

Generated-by: GLM-5.3-Flash (ZCode)
2026-10-10 20:52:24 +10:00

124 lines
4.3 KiB
JavaScript

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { execFile } from 'node:child_process';
import { mkdtempSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { test } from 'node:test';
import { promisify } from 'node:util';
const run = promisify(execFile);
const script = new URL('./check-allow-scripts.mjs', import.meta.url).pathname.replace(
/^\/([A-Za-z]:)/,
'$1',
);
function writeFixture({ allowScripts, lockPackages }) {
const dir = mkdtempSync(path.join(tmpdir(), 'allow-scripts-'));
const packageJsonPath = path.join(dir, 'package.json');
const lockfilePath = path.join(dir, 'package-lock.json');
writeFileSync(packageJsonPath, JSON.stringify({ allowScripts }));
writeFileSync(lockfilePath, JSON.stringify({ packages: lockPackages }));
return { packageJsonPath, lockfilePath };
}
async function runCheck(fixture) {
try {
const { stdout } = await run(process.execPath, [
script,
'--package',
fixture.packageJsonPath,
'--lock',
fixture.lockfilePath,
]);
return { code: 0, stdout };
} catch (error) {
return { code: error.code, stdout: '', stderr: String(error.stderr) };
}
}
const BASE_LOCK = {
'': { name: 'maka', version: '0.2.0', hasInstallScript: true },
'node_modules/esbuild': { version: '0.28.2', hasInstallScript: true },
'node_modules/fsevents': {
version: '2.3.3',
hasInstallScript: true,
optional: true,
os: ['darwin'],
},
'node_modules/node-pty': { version: '1.2.0-beta.15', hasInstallScript: true },
};
test('a synced map passes', async () => {
const fixture = writeFixture({
allowScripts: {
'esbuild@0.28.2': true,
'fsevents@2.3.3': true,
'node-pty@1.2.0-beta.15': true,
},
lockPackages: BASE_LOCK,
});
const result = await runCheck(fixture);
if (result.code !== 0) throw new Error(`expected exit 0, got ${result.code}: ${result.stderr}`);
});
test('an unkeyed optional darwin-only package fails (the fsevents case)', async () => {
const fixture = writeFixture({
allowScripts: { 'esbuild@0.28.2': true, 'node-pty@1.2.0-beta.15': true },
lockPackages: BASE_LOCK,
});
const result = await runCheck(fixture);
if (result.code !== 1) throw new Error(`expected exit 1, got ${result.code}`);
if (!result.stderr.includes('fsevents@2.3.3 (optional, os: darwin)'))
throw new Error(`missing fsevents note: ${result.stderr}`);
});
test('a map key trailing a lockfile bump fails (the stale-version case)', async () => {
const fixture = writeFixture({
allowScripts: {
'esbuild@0.27.7': true,
'fsevents@2.3.3': true,
'node-pty@1.2.0-beta.15': true,
},
lockPackages: BASE_LOCK,
});
const result = await runCheck(fixture);
if (result.code !== 1) throw new Error(`expected exit 1, got ${result.code}`);
if (!result.stderr.includes('stale: "esbuild@0.27.7"'))
throw new Error(`missing stale entry: ${result.stderr}`);
if (!result.stderr.includes('missing: esbuild@0.28.2'))
throw new Error(`missing missing-entry: ${result.stderr}`);
});
test('a non-boolean value fails', async () => {
const fixture = writeFixture({
allowScripts: {
'esbuild@0.28.2': 'yes',
'fsevents@2.3.3': true,
'node-pty@1.2.0-beta.15': true,
},
lockPackages: BASE_LOCK,
});
const result = await runCheck(fixture);
if (result.code !== 1) throw new Error(`expected exit 1, got ${result.code}`);
if (!result.stderr.includes('"esbuild@0.28.2" maps to "yes"'))
throw new Error(`missing invalid value: ${result.stderr}`);
});