This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { checkLockfilePolicy } from './dependency-policy.mjs';
|
||||
import { rebuildArgs, validateRebuildEntries, verify } from './rebuild-native.mjs';
|
||||
|
||||
const registryOrigin = 'https://registry.npmjs.org/';
|
||||
|
||||
describe('dependency policy adversarial cases', () => {
|
||||
it('rejects a non-link package when resolved and integrity are both missing', () => {
|
||||
const violations = checkLockfilePolicy(
|
||||
{
|
||||
lockfileVersion: 3,
|
||||
packages: {
|
||||
'node_modules/evil': {
|
||||
version: '1.0.0',
|
||||
hasInstallScript: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
['node_modules/evil'],
|
||||
registryOrigin,
|
||||
);
|
||||
|
||||
expect(violations).toContainEqual({
|
||||
code: 'missing-resolved',
|
||||
name: 'node_modules/evil',
|
||||
});
|
||||
expect(violations).toContainEqual({
|
||||
code: 'missing-integrity',
|
||||
name: 'node_modules/evil',
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects lookalike registry hostnames', () => {
|
||||
const violations = checkLockfilePolicy(
|
||||
{
|
||||
lockfileVersion: 3,
|
||||
packages: {
|
||||
'node_modules/evil': {
|
||||
version: '1.0.0',
|
||||
resolved: 'https://registry.npmjs.org.evil.example/evil.tgz',
|
||||
integrity: 'sha512-example',
|
||||
},
|
||||
},
|
||||
},
|
||||
[],
|
||||
registryOrigin,
|
||||
);
|
||||
|
||||
expect(violations).toContainEqual({
|
||||
code: 'non-registry-url',
|
||||
name: 'node_modules/evil',
|
||||
});
|
||||
});
|
||||
|
||||
// このテストは名前で path / name / version / integrity の 4 つを縛ると言っている。
|
||||
// ところが実際に起こしていたのは version の食い違いだけで、integrity 照合を
|
||||
// 削っても 35 件すべて緑だった(PR #1074 のレビュー M8)。4 つを別々に起こす。
|
||||
const soundLockfile = (over = {}) => ({
|
||||
lockfileVersion: 3,
|
||||
packages: {
|
||||
'node_modules/native': {
|
||||
version: '1.0.0',
|
||||
resolved: 'https://registry.npmjs.org/native/-/native-1.0.0.tgz',
|
||||
integrity: 'sha512-approved',
|
||||
hasInstallScript: true,
|
||||
...over,
|
||||
},
|
||||
},
|
||||
});
|
||||
const soundApproval = (over = {}) => [{
|
||||
path: 'node_modules/native',
|
||||
name: 'native',
|
||||
version: '1.0.0',
|
||||
integrity: 'sha512-approved',
|
||||
verifier: 'better-sqlite3-memory-db',
|
||||
...over,
|
||||
}];
|
||||
|
||||
it('accepts an approval that matches the lockfile on every field', () => {
|
||||
expect(validateRebuildEntries(soundLockfile(), soundApproval())).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('rejects an integrity mismatch even when the version agrees', () => {
|
||||
expect(() => validateRebuildEntries(soundLockfile({ integrity: 'sha512-swapped' }), soundApproval()))
|
||||
.toThrow(/integrity mismatch/i);
|
||||
});
|
||||
|
||||
it('rejects a target the lockfile says has no install script', () => {
|
||||
// 許可一覧に「script を持たない依存」を書けると、rebuild だけが走る経路になる
|
||||
expect(() => validateRebuildEntries(soundLockfile({ hasInstallScript: false }), soundApproval()))
|
||||
.toThrow(/no install script/i);
|
||||
});
|
||||
|
||||
it('rejects a path whose last node_modules segment is not the approved name', () => {
|
||||
// 経路の取り出しは lastIndexOf でなければならない。indexOf に変えると
|
||||
// node_modules/a/node_modules/b が "a/node_modules/b" になり、
|
||||
// 名前の食い違いを見逃す
|
||||
expect(() => validateRebuildEntries(
|
||||
{
|
||||
lockfileVersion: 3,
|
||||
packages: {
|
||||
'node_modules/outer/node_modules/native': {
|
||||
version: '1.0.0',
|
||||
resolved: 'https://registry.npmjs.org/native/-/native-1.0.0.tgz',
|
||||
integrity: 'sha512-approved',
|
||||
hasInstallScript: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
soundApproval({ path: 'node_modules/outer/node_modules/native', name: 'outer' }),
|
||||
)).toThrow(/name mismatch/i);
|
||||
});
|
||||
|
||||
it('accepts a nested path when the approved name matches its last segment', () => {
|
||||
expect(validateRebuildEntries(
|
||||
{
|
||||
lockfileVersion: 3,
|
||||
packages: {
|
||||
'node_modules/outer/node_modules/native': {
|
||||
version: '1.0.0',
|
||||
resolved: 'https://registry.npmjs.org/native/-/native-1.0.0.tgz',
|
||||
integrity: 'sha512-approved',
|
||||
hasInstallScript: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
soundApproval({ path: 'node_modules/outer/node_modules/native' }),
|
||||
)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('refuses a verifier it does not know, instead of silently skipping', () => {
|
||||
// verify を即 return にしても 35 件すべて緑だった(レビュー M7)。
|
||||
// 「実際にデータベースを開いて確かめる」が丸ごと消えても誰も気づかない状態。
|
||||
expect(() => verify('no-such-verifier')).toThrow(/unsupported native rebuild verifier/i);
|
||||
});
|
||||
|
||||
it('actually opens a database for the verifier this repository uses', () => {
|
||||
// 中身が消えたら(即 return になったら)ここが空振りになるので、
|
||||
// 検証が本当に走ったことを、走った先の失敗で見る
|
||||
expect(() => verify('better-sqlite3-memory-db')).not.toThrow();
|
||||
});
|
||||
|
||||
it('binds native rebuild permission to path, name, version, and integrity', () => {
|
||||
const lockfile = {
|
||||
lockfileVersion: 3,
|
||||
packages: {
|
||||
'node_modules/native': {
|
||||
version: '2.0.0',
|
||||
resolved: 'https://registry.npmjs.org/native/-/native-2.0.0.tgz',
|
||||
integrity: 'sha512-new',
|
||||
hasInstallScript: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const approved = [{
|
||||
path: 'node_modules/native',
|
||||
name: 'native',
|
||||
version: '1.0.0',
|
||||
integrity: 'sha512-approved',
|
||||
verifier: 'better-sqlite3-memory-db',
|
||||
}];
|
||||
|
||||
expect(() => validateRebuildEntries(lockfile, approved)).toThrow(/version mismatch/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe('how npm rebuild is invoked', () => {
|
||||
// 引数の並びに意味がある箇所。PR #1074 のレビューが、`--` を消しても
|
||||
// 35 件すべて緑だったと報告した(M6)。
|
||||
it('separates the package name from flags with --', () => {
|
||||
const args = rebuildArgs('better-sqlite3');
|
||||
expect(args[args.length - 2]).toBe('--');
|
||||
expect(args[args.length - 1]).toBe('better-sqlite3');
|
||||
});
|
||||
|
||||
it('builds from source instead of downloading a prebuilt binary', () => {
|
||||
// 配布物を取ってこないぶん安全側。消すと取得物が 1 つ増える
|
||||
expect(rebuildArgs('better-sqlite3')).toContain('--build-from-source');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user