mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
200 lines
5.9 KiB
JavaScript
200 lines
5.9 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { test } = require('node:test');
|
|
const {
|
|
LocalOwnerPepperConfigurationError,
|
|
LocalOwnerPepperConflictError,
|
|
LocalOwnerPepperUnavailableError,
|
|
backupLocalOwnerPepper,
|
|
inspectLocalOwnerPepper,
|
|
provisionLocalOwnerPepper,
|
|
restoreLocalOwnerPepper,
|
|
} = require('../dist/pepper-custody');
|
|
|
|
function fixture(t) {
|
|
const deploymentRoot = fs.mkdtempSync(
|
|
path.join(os.tmpdir(), 'ql3-owner-pepper-'),
|
|
);
|
|
fs.chmodSync(deploymentRoot, 0o700);
|
|
const backupRoot = fs.mkdtempSync(
|
|
path.join(os.tmpdir(), 'ql3-owner-pepper-backup-'),
|
|
);
|
|
fs.chmodSync(backupRoot, 0o700);
|
|
t.after(() => {
|
|
fs.rmSync(deploymentRoot, { recursive: true, force: true });
|
|
fs.rmSync(backupRoot, { recursive: true, force: true });
|
|
});
|
|
return {
|
|
deploymentRoot,
|
|
backupRoot,
|
|
pepperPath: path.join(deploymentRoot, 'owner.pepper'),
|
|
backupPath: path.join(backupRoot, 'owner.pepper.backup'),
|
|
};
|
|
}
|
|
|
|
test('provisions one canonical private pepper without replacement', (t) => {
|
|
const value = fixture(t);
|
|
const entropy = Buffer.alloc(32, 41);
|
|
const result = provisionLocalOwnerPepper({
|
|
deploymentRoot: value.deploymentRoot,
|
|
pepperPath: value.pepperPath,
|
|
randomBytes() {
|
|
return entropy;
|
|
},
|
|
});
|
|
assert.equal(result.version, 1);
|
|
assert.equal(result.byteLength, 43);
|
|
assert.match(result.digest, /^[0-9a-f]{64}$/);
|
|
assert.equal(fs.statSync(value.pepperPath).mode & 0o777, 0o600);
|
|
assert.equal(
|
|
fs.readFileSync(value.pepperPath, 'utf8'),
|
|
Buffer.alloc(32, 41).toString('base64url'),
|
|
);
|
|
assert.equal(entropy.equals(Buffer.alloc(32)), true);
|
|
assert.deepEqual(
|
|
inspectLocalOwnerPepper({
|
|
deploymentRoot: value.deploymentRoot,
|
|
pepperPath: value.pepperPath,
|
|
}),
|
|
result,
|
|
);
|
|
|
|
const before = fs.readFileSync(value.pepperPath);
|
|
assert.throws(
|
|
() =>
|
|
provisionLocalOwnerPepper({
|
|
deploymentRoot: value.deploymentRoot,
|
|
pepperPath: value.pepperPath,
|
|
}),
|
|
LocalOwnerPepperConflictError,
|
|
);
|
|
assert.deepEqual(fs.readFileSync(value.pepperPath), before);
|
|
});
|
|
|
|
test('creates an independent no-replace backup and restores only to absence', (t) => {
|
|
const value = fixture(t);
|
|
const provisioned = provisionLocalOwnerPepper({
|
|
deploymentRoot: value.deploymentRoot,
|
|
pepperPath: value.pepperPath,
|
|
randomBytes: () => Buffer.alloc(32, 42),
|
|
});
|
|
const backedUp = backupLocalOwnerPepper(value);
|
|
assert.deepEqual(backedUp, provisioned);
|
|
const pepperStat = fs.statSync(value.pepperPath, { bigint: true });
|
|
const backupStat = fs.statSync(value.backupPath, { bigint: true });
|
|
assert.equal(backupStat.mode & 0o777n, 0o600n);
|
|
assert.notEqual(backupStat.ino, pepperStat.ino);
|
|
assert.deepEqual(
|
|
fs.readFileSync(value.backupPath),
|
|
fs.readFileSync(value.pepperPath),
|
|
);
|
|
|
|
fs.unlinkSync(value.pepperPath);
|
|
const restored = restoreLocalOwnerPepper(value);
|
|
assert.deepEqual(restored, provisioned);
|
|
assert.deepEqual(
|
|
fs.readFileSync(value.pepperPath),
|
|
fs.readFileSync(value.backupPath),
|
|
);
|
|
assert.throws(
|
|
() => restoreLocalOwnerPepper(value),
|
|
LocalOwnerPepperConflictError,
|
|
);
|
|
});
|
|
|
|
test('never overwrites a pre-existing backup', (t) => {
|
|
const value = fixture(t);
|
|
provisionLocalOwnerPepper({
|
|
deploymentRoot: value.deploymentRoot,
|
|
pepperPath: value.pepperPath,
|
|
randomBytes: () => Buffer.alloc(32, 43),
|
|
});
|
|
fs.writeFileSync(value.backupPath, 'reserved', { mode: 0o600 });
|
|
assert.throws(
|
|
() => backupLocalOwnerPepper(value),
|
|
LocalOwnerPepperConflictError,
|
|
);
|
|
assert.equal(fs.readFileSync(value.backupPath, 'utf8'), 'reserved');
|
|
});
|
|
|
|
test('fails closed for broad roots, symlink parents and tampered pepper files', (t) => {
|
|
const broad = fixture(t);
|
|
fs.chmodSync(broad.deploymentRoot, 0o755);
|
|
assert.throws(
|
|
() =>
|
|
provisionLocalOwnerPepper({
|
|
deploymentRoot: broad.deploymentRoot,
|
|
pepperPath: broad.pepperPath,
|
|
}),
|
|
LocalOwnerPepperUnavailableError,
|
|
);
|
|
|
|
const linked = fixture(t);
|
|
const actual = path.join(linked.deploymentRoot, 'actual');
|
|
fs.mkdirSync(actual, { mode: 0o700 });
|
|
const alias = path.join(linked.deploymentRoot, 'alias');
|
|
fs.symlinkSync(actual, alias);
|
|
assert.throws(
|
|
() =>
|
|
provisionLocalOwnerPepper({
|
|
deploymentRoot: linked.deploymentRoot,
|
|
pepperPath: path.join(alias, 'owner.pepper'),
|
|
}),
|
|
LocalOwnerPepperUnavailableError,
|
|
);
|
|
|
|
const tampered = fixture(t);
|
|
provisionLocalOwnerPepper({
|
|
deploymentRoot: tampered.deploymentRoot,
|
|
pepperPath: tampered.pepperPath,
|
|
});
|
|
fs.chmodSync(tampered.pepperPath, 0o644);
|
|
assert.throws(
|
|
() =>
|
|
inspectLocalOwnerPepper({
|
|
deploymentRoot: tampered.deploymentRoot,
|
|
pepperPath: tampered.pepperPath,
|
|
}),
|
|
LocalOwnerPepperUnavailableError,
|
|
);
|
|
});
|
|
|
|
test('rejects invalid entropy and widened options before publishing', (t) => {
|
|
const value = fixture(t);
|
|
assert.throws(
|
|
() =>
|
|
provisionLocalOwnerPepper({
|
|
deploymentRoot: value.deploymentRoot,
|
|
pepperPath: value.pepperPath,
|
|
randomBytes: () => Buffer.alloc(31),
|
|
}),
|
|
LocalOwnerPepperConfigurationError,
|
|
);
|
|
assert.equal(fs.existsSync(value.pepperPath), false);
|
|
assert.throws(
|
|
() =>
|
|
provisionLocalOwnerPepper({
|
|
deploymentRoot: value.deploymentRoot,
|
|
pepperPath: value.pepperPath,
|
|
extra: true,
|
|
}),
|
|
LocalOwnerPepperConfigurationError,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
provisionLocalOwnerPepper({
|
|
deploymentRoot: value.deploymentRoot,
|
|
pepperPath: value.pepperPath,
|
|
randomBytes() {
|
|
throw new Error('sensitive entropy provider detail');
|
|
},
|
|
}),
|
|
(error) =>
|
|
error instanceof LocalOwnerPepperUnavailableError &&
|
|
error.message === 'Local Owner pepper operation is unavailable',
|
|
);
|
|
assert.equal(fs.existsSync(value.pepperPath), false);
|
|
});
|