mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-23 03:18:09 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { createHash } = require('node:crypto');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { test } = require('node:test');
|
||||
const {
|
||||
openLocalSqliteBootstrapDatabase,
|
||||
} = require('@qinglong/local-sqlite/bootstrap');
|
||||
const { migrateLocalSqlitePath } = require('@qinglong/local-sqlite/migration');
|
||||
const {
|
||||
LocalOwnerCliConfigurationError,
|
||||
createLocalOwnerCommandRunner,
|
||||
} = require('@qinglong/local-owner-cli');
|
||||
|
||||
async function fixture(t) {
|
||||
const deploymentRoot = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), 'ql3-owner-cli-'),
|
||||
);
|
||||
fs.chmodSync(deploymentRoot, 0o700);
|
||||
t.after(() => fs.rmSync(deploymentRoot, { recursive: true, force: true }));
|
||||
const databasePath = path.join(deploymentRoot, 'qinglong3.sqlite');
|
||||
const pepperPath = path.join(deploymentRoot, 'owner.pepper');
|
||||
const secretDeliveryDirectory = path.join(deploymentRoot, 'secrets');
|
||||
const commandsDirectory = path.join(deploymentRoot, 'commands');
|
||||
fs.mkdirSync(secretDeliveryDirectory, { mode: 0o700 });
|
||||
fs.mkdirSync(commandsDirectory, { mode: 0o700 });
|
||||
fs.writeFileSync(pepperPath, Buffer.alloc(32, 83).toString('base64url'), {
|
||||
mode: 0o600,
|
||||
});
|
||||
const options = {
|
||||
deploymentRoot,
|
||||
databasePath,
|
||||
pepperPath,
|
||||
pepperKeyId: 'legacy-v1',
|
||||
secretDeliveryDirectory,
|
||||
profile: 'edge',
|
||||
};
|
||||
await migrateLocalSqlitePath(options);
|
||||
const material = fs.readFileSync(pepperPath);
|
||||
const materialDigest = createHash('sha256')
|
||||
.update('qinglong.local-owner-pepper.summary.v1\0', 'utf8')
|
||||
.update(material)
|
||||
.digest('hex');
|
||||
material.fill(0);
|
||||
const database = await openLocalSqliteBootstrapDatabase(options);
|
||||
await database.ownerPepper.register({
|
||||
mutationId: '00000000-0000-4000-8000-000000000c91',
|
||||
pepperKeyId: 'legacy-v1',
|
||||
materialDigest,
|
||||
backupDigest: 'b'.repeat(64),
|
||||
registeredAtMs: 1,
|
||||
});
|
||||
await database.ownerPepper.activate({
|
||||
mutationId: '00000000-0000-4000-8000-000000000c92',
|
||||
pepperKeyId: 'legacy-v1',
|
||||
expectedGeneration: 0,
|
||||
activatedAtMs: 2,
|
||||
});
|
||||
await database.close();
|
||||
return { options, commandsDirectory };
|
||||
}
|
||||
|
||||
function commandFile(state, operation, request, suffix) {
|
||||
const filePath = path.join(state.commandsDirectory, `${suffix}.json`);
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
`${JSON.stringify({
|
||||
schemaVersion: 1,
|
||||
operation,
|
||||
options: state.options,
|
||||
request,
|
||||
})}\n`,
|
||||
{ mode: 0o600 },
|
||||
);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function assertNoSecretFields(value) {
|
||||
if (!value || typeof value !== 'object') return;
|
||||
for (const [key, nested] of Object.entries(value)) {
|
||||
assert.doesNotMatch(key, /secret|token/i);
|
||||
assertNoSecretFields(nested);
|
||||
}
|
||||
}
|
||||
|
||||
test('completes fresh Owner and credential recovery ceremonies without returning secrets', async (t) => {
|
||||
const state = await fixture(t);
|
||||
const runner = createLocalOwnerCommandRunner();
|
||||
const credentialMutationId = '00000000-0000-4000-8000-000000000c01';
|
||||
const challengeMutationId = '00000000-0000-4000-8000-000000000c02';
|
||||
const claimMutationId = '00000000-0000-4000-8000-000000000c03';
|
||||
const provisioned = await runner.run(
|
||||
commandFile(
|
||||
state,
|
||||
'owner.identity.provision',
|
||||
{
|
||||
mutationId: credentialMutationId,
|
||||
requestId: 'owner-cli-provision-c01',
|
||||
},
|
||||
'01-provision',
|
||||
),
|
||||
);
|
||||
assert.equal(provisioned.status, 'inserted');
|
||||
assert.equal(provisioned.delivery.kind, 'credential');
|
||||
const issued = await runner.run(
|
||||
commandFile(
|
||||
state,
|
||||
'owner.challenge.issue',
|
||||
{
|
||||
projectId: 'default',
|
||||
mutationId: challengeMutationId,
|
||||
requestId: 'owner-cli-issue-c02',
|
||||
},
|
||||
'02-issue',
|
||||
),
|
||||
);
|
||||
assert.equal(issued.delivery.kind, 'challenge');
|
||||
const inspected = await runner.run(
|
||||
commandFile(
|
||||
state,
|
||||
'owner.delivery.inspect',
|
||||
{ kind: 'challenge', mutationId: challengeMutationId },
|
||||
'03-inspect',
|
||||
),
|
||||
);
|
||||
assert.equal(
|
||||
inspected.delivery.deliveryDigest,
|
||||
issued.delivery.deliveryDigest,
|
||||
);
|
||||
const claimFile = commandFile(
|
||||
state,
|
||||
'owner.claim.from-deliveries',
|
||||
{
|
||||
projectId: 'default',
|
||||
mutationId: claimMutationId,
|
||||
requestId: 'owner-cli-claim-c03',
|
||||
credentialMutationId,
|
||||
challengeMutationId,
|
||||
},
|
||||
'04-claim',
|
||||
);
|
||||
const claimed = await runner.run(claimFile);
|
||||
assert.equal(claimed.status, 'inserted');
|
||||
assert.equal(claimed.role, 'owner');
|
||||
assert.equal(JSON.stringify(claimed).includes('secret'), false);
|
||||
for (const [purpose, mutationId, digest, suffix] of [
|
||||
[
|
||||
'credential-provisioning',
|
||||
credentialMutationId,
|
||||
provisioned.delivery.deliveryDigest,
|
||||
'05-ack-credential',
|
||||
],
|
||||
[
|
||||
'challenge',
|
||||
challengeMutationId,
|
||||
issued.delivery.deliveryDigest,
|
||||
'06-ack-challenge',
|
||||
],
|
||||
]) {
|
||||
const acknowledged = await runner.run(
|
||||
commandFile(
|
||||
state,
|
||||
'owner.delivery.acknowledge',
|
||||
{ purpose, mutationId, expectedDeliveryDigest: digest },
|
||||
suffix,
|
||||
),
|
||||
);
|
||||
assert.equal(acknowledged.mutationId, mutationId);
|
||||
}
|
||||
const replay = await runner.run(claimFile);
|
||||
assert.equal(replay.status, 'existing');
|
||||
const recoveryMutationId = '00000000-0000-4000-8000-000000000c04';
|
||||
const recovery = await runner.run(
|
||||
commandFile(
|
||||
state,
|
||||
'owner.credential-recovery.issue',
|
||||
{
|
||||
mutationId: recoveryMutationId,
|
||||
requestId: 'owner-cli-recovery-c04',
|
||||
previousCredentialId: provisioned.credentialId,
|
||||
expectedPreviousVersion: 1,
|
||||
},
|
||||
'07-recovery-issue',
|
||||
),
|
||||
);
|
||||
assert.equal(recovery.state, 'issued');
|
||||
assert.equal(recovery.delivery.kind, 'credential');
|
||||
await runner.run(
|
||||
commandFile(
|
||||
state,
|
||||
'owner.delivery.acknowledge',
|
||||
{
|
||||
purpose: 'credential-recovery',
|
||||
mutationId: recoveryMutationId,
|
||||
expectedDeliveryDigest: recovery.delivery.deliveryDigest,
|
||||
},
|
||||
'08-recovery-ack',
|
||||
),
|
||||
);
|
||||
const completed = await runner.run(
|
||||
commandFile(
|
||||
state,
|
||||
'owner.credential-recovery.complete',
|
||||
{
|
||||
issueMutationId: recoveryMutationId,
|
||||
mutationId: '00000000-0000-4000-8000-000000000c05',
|
||||
requestId: 'owner-cli-recovery-complete-c05',
|
||||
},
|
||||
'09-recovery-complete',
|
||||
),
|
||||
);
|
||||
assert.equal(completed.state, 'completed');
|
||||
for (const result of [
|
||||
provisioned,
|
||||
issued,
|
||||
inspected,
|
||||
claimed,
|
||||
recovery,
|
||||
completed,
|
||||
]) {
|
||||
assertNoSecretFields(result);
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects widened command intent and exposes only a command-file binary', async (t) => {
|
||||
const state = await fixture(t);
|
||||
const widened = commandFile(
|
||||
state,
|
||||
'owner.delivery.inspect',
|
||||
{
|
||||
kind: 'credential',
|
||||
mutationId: '00000000-0000-4000-8000-000000000d01',
|
||||
secret: 'forbidden',
|
||||
},
|
||||
'widened',
|
||||
);
|
||||
await assert.rejects(
|
||||
createLocalOwnerCommandRunner().run(widened),
|
||||
LocalOwnerCliConfigurationError,
|
||||
);
|
||||
const help = spawnSync(
|
||||
process.execPath,
|
||||
[path.join(__dirname, '../dist/cli.js'), '--help'],
|
||||
{ encoding: 'utf8' },
|
||||
);
|
||||
assert.equal(help.status, 0);
|
||||
assert.match(help.stdout, /^Usage: ql3-owner run --command-file /);
|
||||
const invalid = spawnSync(
|
||||
process.execPath,
|
||||
[path.join(__dirname, '../dist/cli.js'), 'run'],
|
||||
{ encoding: 'utf8' },
|
||||
);
|
||||
assert.equal(invalid.status, 64);
|
||||
assert.equal(
|
||||
JSON.parse(invalid.stderr).code,
|
||||
'LOCAL_OWNER_CLI_USAGE_INVALID',
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user