chore: adopt node-addon-landlock-run source as native/ subtree
Bring the node-addon-landlock-run tree (tag v0.0.1, commit 614f7fd) into native/landlock-run as its source of record: launcher development happens here, next to the harness consumers, and the standalone repository becomes the release mirror the tree is exported to for packing and publishing (procedure in native/README.md). The subtree keeps its own pnpm workspace and lockfile and is NOT added to the harness workspace: harness installs, gates, and CI never touch it. The mirror's .github/ stays out of the subtree; a separate manually-dispatched workflow (.github/workflows/landlock-run.yml) runs the subtree's CI legs — the per-architecture native builds, real-kernel launcher proofs, and pack rehearsal — adapted with working-directory/cache paths. eslint ignores the subtree like vendor/; AGENTS.md gains the native/ layout line (+5 words on its budget ceiling).
This commit is contained in:
76
native/landlock-run/test/entry.test.js
Normal file
76
native/landlock-run/test/entry.test.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Keyless entry-package tests — run on every host, no kernel or binary
|
||||
* required. Cover the JS seam's pure surface: grant-argv construction, the
|
||||
* resolution contract (platform package → fallback), and probe verdicts over
|
||||
* fake launchers. Requires built `lib/` (`pnpm build:ts`).
|
||||
*/
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import {
|
||||
LAUNCHER_BIN,
|
||||
LAUNCHER_FAILURE_EXIT,
|
||||
grantArgs,
|
||||
launcherPath,
|
||||
probe,
|
||||
} from 'node-addon-landlock-run';
|
||||
|
||||
// --- constants are part of the CLI contract ---
|
||||
assert.equal(LAUNCHER_BIN, 'landlock-run');
|
||||
assert.equal(LAUNCHER_FAILURE_EXIT, 125);
|
||||
|
||||
// --- grantArgs: flag spelling, ordering, and empty grants ---
|
||||
assert.deepEqual(grantArgs({}), []);
|
||||
assert.deepEqual(grantArgs({ readOnly: ['/'] }), ['--ro', '/']);
|
||||
assert.deepEqual(
|
||||
grantArgs({ readOnly: ['/', '/opt'], readWrite: ['/tmp/work'] }),
|
||||
['--ro', '/', '--ro', '/opt', '--rw', '/tmp/work'],
|
||||
);
|
||||
assert.deepEqual(grantArgs({ readWrite: ['/a'], readOnly: ['/b'] }), ['--ro', '/b', '--rw', '/a']);
|
||||
|
||||
// --- launcherPath: resolves the platform package next to its package.json ---
|
||||
const platformPackage = `node-addon-landlock-run-${process.platform}-${process.arch}`;
|
||||
const resolvedViaSeam = launcherPath((specifier) => {
|
||||
assert.equal(specifier, `${platformPackage}/package.json`);
|
||||
return path.join('/fake-install', specifier);
|
||||
});
|
||||
assert.equal(resolvedViaSeam, path.join('/fake-install', platformPackage, 'bin', LAUNCHER_BIN));
|
||||
|
||||
// --- launcherPath: unresolvable package falls back to an absolute, package-boundary path ---
|
||||
const fallback = launcherPath(() => {
|
||||
throw new Error('not installed');
|
||||
});
|
||||
assert.ok(path.isAbsolute(fallback), 'fallback path must be absolute');
|
||||
assert.ok(
|
||||
fallback.includes(path.join('node_modules', ...platformPackage.split('/'), 'bin', LAUNCHER_BIN)),
|
||||
`fallback must point at the platform package layout: ${fallback}`,
|
||||
);
|
||||
|
||||
// --- launcherPath: default resolution agrees with this workspace's layout ---
|
||||
const defaultPath = launcherPath();
|
||||
assert.ok(path.isAbsolute(defaultPath));
|
||||
assert.ok(defaultPath.endsWith(path.join('bin', LAUNCHER_BIN)), defaultPath);
|
||||
|
||||
// --- probe: a missing launcher is unusable, indistinguishable from an unenforcing kernel ---
|
||||
assert.equal(probe(path.join(os.tmpdir(), 'nalr-no-such-launcher')), 'unusable');
|
||||
|
||||
// --- probe: verdict parsing over fake launchers (POSIX shells only) ---
|
||||
if (process.platform !== 'win32') {
|
||||
const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nalr-fake-launcher-'));
|
||||
const fake = (name, script) => {
|
||||
const file = path.join(fakeDir, name);
|
||||
fs.writeFileSync(file, `#!/bin/sh\n${script}\n`, { mode: 0o755 });
|
||||
return file;
|
||||
};
|
||||
|
||||
assert.equal(probe(fake('full', 'echo "landlock: fully enforced"; exit 0')), 'full');
|
||||
assert.equal(probe(fake('partial', 'echo "landlock: partially enforced (older ABI)"; exit 0')), 'partial');
|
||||
assert.equal(probe(fake('failing', `exit ${LAUNCHER_FAILURE_EXIT}`)), 'unusable');
|
||||
assert.equal(probe(fake('hanging', 'sleep 10'), { timeoutMs: 200 }), 'unusable');
|
||||
|
||||
fs.rmSync(fakeDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
console.log('entry.test: ok');
|
||||
121
native/landlock-run/test/launcher.test.js
Normal file
121
native/landlock-run/test/launcher.test.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Behavioral tests against the REAL launcher binary on a real kernel: the
|
||||
* CLI contract (usage errors, exit codes, argv passthrough) and the
|
||||
* confinement world-proofs (denied writes stay off disk, grants land).
|
||||
*
|
||||
* Preconditions and their skip semantics:
|
||||
* - Non-Linux host: skips entirely (exit 0) — there is nothing to build here.
|
||||
* - Linux without the built binary: FAILS — run `pnpm build:native` first.
|
||||
* - Linux whose kernel does not enforce Landlock: skips the enforcement
|
||||
* half, unless `NALR_REQUIRE_LANDLOCK=1` (set on CI, where a silent skip on
|
||||
* the very platform that exists to prove enforcement would be a false
|
||||
* green).
|
||||
*/
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import {
|
||||
LAUNCHER_FAILURE_EXIT,
|
||||
grantArgs,
|
||||
launcherPath,
|
||||
probe,
|
||||
} from 'node-addon-landlock-run';
|
||||
|
||||
const requireLandlock = process.env.NALR_REQUIRE_LANDLOCK === '1';
|
||||
|
||||
if (process.platform !== 'linux') {
|
||||
console.log(`launcher.test: SKIP — the launcher only exists on linux (host: ${process.platform})`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const launcher = launcherPath();
|
||||
assert.ok(
|
||||
fs.existsSync(launcher),
|
||||
`launcher.test: no built launcher at ${launcher} — run \`pnpm build:native\` (apt-get install musl-tools) first`,
|
||||
);
|
||||
|
||||
const run = (args, options = {}) => spawnSync(launcher, args, { encoding: 'utf8', ...options });
|
||||
|
||||
// --- usage errors: parse failures exit LAUNCHER_FAILURE_EXIT before any restriction ---
|
||||
{
|
||||
const noCommand = run([]);
|
||||
assert.equal(noCommand.status, LAUNCHER_FAILURE_EXIT);
|
||||
assert.match(noCommand.stderr, /usage error: missing `-- <argv>\.\.\.` command/);
|
||||
|
||||
const unknownFlag = run(['--bogus', '--', 'true']);
|
||||
assert.equal(unknownFlag.status, LAUNCHER_FAILURE_EXIT);
|
||||
assert.match(unknownFlag.stderr, /usage error: unknown argument: --bogus/);
|
||||
|
||||
const danglingPath = run(['--ro']);
|
||||
assert.equal(danglingPath.status, LAUNCHER_FAILURE_EXIT);
|
||||
assert.match(danglingPath.stderr, /--ro requires a path/);
|
||||
|
||||
const probeWithExtras = run(['--probe', '--ro', '/']);
|
||||
assert.equal(probeWithExtras.status, LAUNCHER_FAILURE_EXIT);
|
||||
assert.match(probeWithExtras.stderr, /--probe takes no other arguments/);
|
||||
}
|
||||
|
||||
// --- probe: the functional availability signal ---
|
||||
const enforcement = probe(launcher);
|
||||
console.log(`launcher.test: probe → ${enforcement}`);
|
||||
if (enforcement === 'unusable') {
|
||||
if (requireLandlock) {
|
||||
console.error('launcher.test: NALR_REQUIRE_LANDLOCK=1 but the probe reports unusable — this kernel cannot prove enforcement');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('launcher.test: SKIP enforcement half — kernel does not enforce Landlock');
|
||||
process.exit(0);
|
||||
}
|
||||
{
|
||||
const probeRun = run(['--probe']);
|
||||
assert.equal(probeRun.status, 0);
|
||||
assert.match(probeRun.stdout, /^landlock: (fully enforced|partially enforced \(older ABI\))\n$/);
|
||||
}
|
||||
|
||||
// --- confined exec: the command runs, its exit code passes through ---
|
||||
{
|
||||
const echo = run([...grantArgs({ readOnly: ['/'] }), '--', '/bin/sh', '-c', 'echo confined-ok']);
|
||||
assert.equal(echo.status, 0, echo.stderr);
|
||||
assert.equal(echo.stdout, 'confined-ok\n');
|
||||
|
||||
const exitCode = run([...grantArgs({ readOnly: ['/'] }), '--', '/bin/sh', '-c', 'exit 7']);
|
||||
assert.equal(exitCode.status, 7, 'the wrapped command exit code must pass through unchanged');
|
||||
}
|
||||
|
||||
// --- world-proofs: denied writes stay off disk, grants land, inheritance crosses exec ---
|
||||
{
|
||||
const work = fs.mkdtempSync(path.join(os.tmpdir(), 'nalr-launcher-test-'));
|
||||
|
||||
const denied = path.join(work, 'denied.txt');
|
||||
const deniedRun = run([...grantArgs({ readOnly: ['/'] }), '--', '/bin/sh', '-c', `echo x > ${denied}`]);
|
||||
assert.notEqual(deniedRun.status, 0, 'a write outside the grants must fail');
|
||||
assert.ok(!fs.existsSync(denied), 'the denied write must not land on disk');
|
||||
|
||||
const granted = path.join(work, 'granted.txt');
|
||||
const grantedRun = run([...grantArgs({ readOnly: ['/'], readWrite: [work] }), '--', '/bin/sh', '-c', `echo ok > ${granted}`]);
|
||||
assert.equal(grantedRun.status, 0, grantedRun.stderr);
|
||||
assert.equal(fs.readFileSync(granted, 'utf8'), 'ok\n');
|
||||
|
||||
// The ruleset is inherited across execve: a CHILD of the wrapped command
|
||||
// is confined too, not just the direct exec target.
|
||||
const nested = path.join(work, 'nested.txt');
|
||||
const nestedRun = run([...grantArgs({ readOnly: ['/'] }), '--', '/bin/sh', '-c', `/bin/sh -c 'echo x > ${nested}'; true`]);
|
||||
assert.equal(nestedRun.status, 0, nestedRun.stderr);
|
||||
assert.ok(!fs.existsSync(nested), 'a denied write from a nested child must not land either');
|
||||
|
||||
fs.rmSync(work, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// --- fail closed: an unopenable grant root refuses to exec at all ---
|
||||
{
|
||||
const marker = path.join(os.tmpdir(), `nalr-should-not-exist-${process.pid}`);
|
||||
const badGrant = run(['--ro', '/no/such/grant/root', '--', '/bin/sh', '-c', `echo x > ${marker}`]);
|
||||
assert.equal(badGrant.status, LAUNCHER_FAILURE_EXIT);
|
||||
assert.match(badGrant.stderr, /cannot open rule path/);
|
||||
assert.ok(!fs.existsSync(marker), 'the command must never run when the launcher fails');
|
||||
}
|
||||
|
||||
console.log('launcher.test: ok');
|
||||
Reference in New Issue
Block a user