fix(ci): restore ql3 multi-profile gates

This commit is contained in:
whyour
2026-08-22 13:44:19 +08:00
parent f1cca9a3ed
commit c8ad5ea368
17 changed files with 320 additions and 111 deletions
+12 -9
View File
@@ -140,6 +140,10 @@ jobs:
run: node -e "if (process.arch !== '${{ matrix.arch }}') throw new Error('unexpected architecture ' + process.arch)"
- name: Install workspace dependencies without lifecycle scripts
run: pnpm install --frozen-lockfile --ignore-scripts
- name: Initialize the public test environment
run: |
cp .env.example .env
mkdir -p data/db
- name: Rebuild the reviewed native SQLite binding
run: pnpm rebuild @whyour/sqlite3
- name: Build benchmark targets once
@@ -855,9 +859,7 @@ jobs:
--volume "${{ github.workspace }}:/workspace:ro"
--workdir /workspace
node:24.18.0-bookworm-slim
node --test --test-concurrency=1
packages/ql3-local-command-file/test/*.test.cjs
packages/ql3-local-owner-*/test/*.test.cjs
node scripts/ql3-local-owner-readonly-contract.cjs --mode=root
- name: Exercise Owner authorities as non-root in a read-only container
run: >-
docker run --rm --read-only
@@ -870,9 +872,7 @@ jobs:
--volume "${{ github.workspace }}:/workspace:ro"
--workdir /workspace
node:24.18.0-bookworm-slim
node --test --test-concurrency=1
packages/ql3-local-command-file/test/*.test.cjs
packages/ql3-local-owner-*/test/*.test.cjs
node scripts/ql3-local-owner-readonly-contract.cjs --mode=nonroot
- name: Build and audit production-only local Profile artifacts
run: |
pnpm audit:artifact:edge:ql3
@@ -963,18 +963,21 @@ jobs:
docker exec ${{ job.services.postgres.id }} psql -U postgres -d postgres -v ON_ERROR_STOP=1 -c "ALTER DATABASE ql3_contract OWNER TO ql3_migration"
- name: Create schema and exact grants through the reviewed migration stream
env:
QL3_TEST_POSTGRES_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract
run: node --test packages/ql3-cluster-postgres/test/postgres.integration.test.cjs
QL3_POSTGRES_MIGRATION_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract
QL3_POSTGRES_TLS_MODE: disable
QL3_POSTGRES_ALLOW_INSECURE: 'true'
run: node packages/ql3-cluster-postgres/dist/migration/migrationCli.js
- name: Test readiness, isolated roles, shared Repositories, rollback and SQLSTATE mapping
env:
QL3_TEST_POSTGRES_MIGRATION_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract
QL3_TEST_POSTGRES_RUNTIME_URL: postgresql://ql3_runtime:ql3_runtime_test@127.0.0.1:5432/ql3_contract
QL3_TEST_POSTGRES_ADMIN_URL: postgresql://ql3_admin:ql3_admin_test@127.0.0.1:5432/ql3_contract
QL3_TEST_POSTGRES_AUTOMATION_MANAGER_URL: postgresql://ql3_automation_manager:ql3_automation_manager_test@127.0.0.1:5432/ql3_contract
QL3_TEST_POSTGRES_RUN_MANAGER_URL: postgresql://ql3_run_manager:ql3_run_manager_test@127.0.0.1:5432/ql3_contract
QL3_TEST_POSTGRES_PACKAGE_MANAGER_URL: postgresql://ql3_package_manager:ql3_package_manager_test@127.0.0.1:5432/ql3_contract
QL3_TEST_POSTGRES_PACKAGE_EXECUTOR_URL: postgresql://ql3_package_executor:ql3_package_executor_test@127.0.0.1:5432/ql3_contract
QL3_TEST_POSTGRES_WORKER_INGRESS_URL: postgresql://ql3_worker_ingress:ql3_worker_ingress_test@127.0.0.1:5432/ql3_contract
run: node --test packages/ql3-cluster-postgres/test/postgres.integration.test.cjs
run: node --test --test-concurrency=1 packages/ql3-cluster-postgres/test/postgres.integration.test.cjs
- name: Test automation management concurrency and post-commit response loss over mTLS
env:
QL3_TEST_POSTGRES_URL: postgresql://ql3_migration:ql3_migration_test@127.0.0.1:5432/ql3_contract
@@ -6,7 +6,6 @@ import {
TaskStartUnavailableError,
normalizeTaskStartCommand,
normalizeTaskStartResult,
type TaskStartAllowedRole,
type TaskStartCommand,
type TaskStartRepository,
type TaskStartResult,
@@ -32,12 +31,6 @@ import {
type Row = Record<string, unknown>;
const ALLOWED_ROLES = new Set<TaskStartAllowedRole>([
'owner',
'admin',
'operator',
]);
function unavailable(options?: ErrorOptions): TaskStartUnavailableError {
return new TaskStartUnavailableError(options);
}
@@ -193,34 +186,30 @@ export class PostgresTaskStartRepository implements TaskStartRepository {
try {
await configurePostgresDefinitionTransaction(client);
began = true;
const project = await client.query<Row>(`
SELECT status AS "projectStatus", version AS "projectVersion"
FROM "ql3"."projects" WHERE id = $1 FOR UPDATE
`, [command.projectId]);
if (project.rows.length === 0) throw new TaskStartNotFoundError();
if (project.rows.length !== 1) throw unavailable();
const binding = await client.query<Row>(`
SELECT version AS "bindingVersion", state AS "bindingState",
role AS "bindingRole"
FROM "ql3"."project_role_bindings"
WHERE project_id = $1 AND subject_type = $2 AND subject_id = $3
ORDER BY version DESC LIMIT 1
FOR SHARE
`, [command.projectId, command.subject.type, command.subject.id]);
const currentProject = project.rows[0]!;
const currentBinding = binding.rows[0];
if (
text(currentProject, 'projectStatus') !== 'active' ||
integer(currentProject, 'projectVersion') !==
command.policyFence.projectVersion ||
!currentBinding ||
integer(currentBinding, 'bindingVersion') !==
command.policyFence.bindingVersion ||
text(currentBinding, 'bindingState') !== 'active' ||
!ALLOWED_ROLES.has(
text(currentBinding, 'bindingRole') as TaskStartAllowedRole,
)
) {
const authorization = await client.query<Row>(
`SELECT "ql3"."lock_run_management_policy_fence"(
$1::varchar, $2::varchar, $3::varchar, $4::integer, $5::integer
) AS "matches"`,
[
command.projectId,
command.subject.type,
command.subject.id,
command.policyFence.projectVersion,
command.policyFence.bindingVersion,
],
);
if (authorization.rows.length !== 1) throw unavailable();
if (authorization.rows[0]?.matches !== true) {
const project = await client.query<Row>(
`SELECT EXISTS (
SELECT 1 FROM "ql3"."projects" WHERE id = $1
) AS "exists"`,
[command.projectId],
);
if (project.rows.length !== 1) throw unavailable();
if (!postgresRequiredBoolean(project.rows[0]!.exists, unavailable)) {
throw new TaskStartNotFoundError();
}
throw new TaskStartFenceRejectedError('authorization_changed');
}
@@ -261,7 +250,6 @@ export class PostgresTaskStartRepository implements TaskStartRepository {
AND revision.task_id = head.task_id
AND revision.revision = head.current_revision
WHERE head.project_id = $1 AND head.task_id = $2
FOR UPDATE OF head
`, [command.projectId, command.taskId]);
if (task.rows.length === 0) throw new TaskStartNotFoundError();
if (task.rows.length !== 1) throw unavailable();
@@ -626,6 +626,8 @@ const adminConnectionString =
const automationManagerConnectionString =
process.env.QL3_TEST_POSTGRES_AUTOMATION_MANAGER_URL ??
migrationConnectionString;
const runManagerConnectionString =
process.env.QL3_TEST_POSTGRES_RUN_MANAGER_URL ?? migrationConnectionString;
const packageManagerConnectionString =
process.env.QL3_TEST_POSTGRES_PACKAGE_MANAGER_URL ??
migrationConnectionString;
@@ -678,6 +680,8 @@ if (!migrationConnectionString) {
? adminConnectionString
: role === 'automation-manager'
? automationManagerConnectionString
: role === 'run-manager'
? runManagerConnectionString
: role === 'package-manager'
? packageManagerConnectionString
: role === 'package-executor'
@@ -1045,7 +1049,7 @@ if (!migrationConnectionString) {
const taskId = 'task-start-command';
const subjectId = 'usr_task_start_integration';
const migrationDatabase = await open('migration');
let runtimeDatabase;
let runManagerDatabase;
try {
await runPostgresMigrations({ pool: migrationDatabase.pool });
await migrationDatabase.pool.query(
@@ -1102,11 +1106,13 @@ if (!migrationConnectionString) {
})
).definition;
runtimeDatabase =
runtimeConnectionString === migrationConnectionString
runManagerDatabase =
runManagerConnectionString === migrationConnectionString
? migrationDatabase
: await open('runtime');
const repository = new PostgresTaskStartRepository(runtimeDatabase.pool);
: await open('run-manager');
const repository = new PostgresTaskStartRepository(
runManagerDatabase.pool,
);
const command = {
projectId,
taskId,
@@ -1158,8 +1164,8 @@ if (!migrationConnectionString) {
},
]);
} finally {
if (runtimeDatabase && runtimeDatabase !== migrationDatabase) {
await runtimeDatabase.close();
if (runManagerDatabase && runManagerDatabase !== migrationDatabase) {
await runManagerDatabase.close();
}
await migrationDatabase.close();
}
@@ -2193,6 +2199,9 @@ if (!migrationConnectionString) {
const migrationDatabase = await open('migration');
try {
await runPostgresMigrations({ pool: migrationDatabase.pool });
await migrationDatabase.pool.query(
'TRUNCATE TABLE "ql3"."runs" CASCADE',
);
await observeContractPublisherTrust(migrationDatabase.pool);
await migrationDatabase.pool.query(
`INSERT INTO "ql3"."projects" (
@@ -2577,6 +2586,9 @@ if (!migrationConnectionString) {
const migrationDatabase = await open('migration');
try {
await runPostgresMigrations({ pool: migrationDatabase.pool });
await migrationDatabase.pool.query(
'TRUNCATE TABLE "ql3"."plugin_package_installs" CASCADE',
);
await observeContractPublisherTrust(migrationDatabase.pool);
await migrationDatabase.pool.query(
`INSERT INTO "ql3"."projects" (
@@ -2589,7 +2601,7 @@ if (!migrationConnectionString) {
await migrationDatabase.close();
}
const executorDatabase = await open('package-executor');
const adminDatabase = await open('admin');
const automationManagerDatabase = await open('automation-manager');
return {
repository: new PostgresPluginPackageTaskReconciliationRepository(
executorDatabase.pool,
@@ -2607,11 +2619,14 @@ if (!migrationConnectionString) {
),
),
taskRepository: new PostgresTaskDefinitionRepository(
adminDatabase.pool,
automationManagerDatabase.pool,
fixture.registry,
),
close: async () => {
await Promise.all([executorDatabase.close(), adminDatabase.close()]);
await Promise.all([
executorDatabase.close(),
automationManagerDatabase.close(),
]);
},
};
},
@@ -136,19 +136,12 @@ function fixture(options = {}) {
normalized.startsWith('SELECT set_config') ||
normalized.startsWith('INSERT INTO')
) return { rows: [], rowCount: normalized.startsWith('INSERT') ? 1 : 0 };
if (normalized.includes('FROM "ql3"."projects"')) {
const rows = options.projectRows ?? [{
projectStatus: 'active',
projectVersion: 2,
}];
if (normalized.includes('lock_run_management_policy_fence')) {
const rows = options.authorizationRows ?? [{ matches: true }];
return { rows, rowCount: rows.length };
}
if (normalized.includes('FROM "ql3"."project_role_bindings"')) {
const rows = options.bindingRows ?? [{
bindingVersion: 3,
bindingState: 'active',
bindingRole: 'operator',
}];
if (normalized.includes('SELECT EXISTS')) {
const rows = options.projectExistenceRows ?? [{ exists: true }];
return { rows, rowCount: rows.length };
}
if (normalized.includes('FROM "ql3"."runs"')) {
@@ -203,11 +196,11 @@ test('revalidates Policy and Task/execution digests before one atomic Run aggreg
executionRevisionDigest: EXECUTION.contentDigest,
createdAtMs: 1_000,
});
const project = calls.findIndex(({ sql }) => sql.includes('FROM "ql3"."projects"'));
const binding = calls.findIndex(({ sql }) => sql.includes('project_role_bindings'));
const authorization = calls.findIndex(({ sql }) =>
sql.includes('lock_run_management_policy_fence'));
const task = calls.findIndex(({ sql }) => sql.includes('task_definitions'));
const execution = calls.findIndex(({ sql }) => sql.includes('task_execution_revisions'));
assert.ok(project < binding && binding < task && task < execution);
assert.ok(authorization < task && task < execution);
assert.equal(calls.filter(({ sql }) => sql.startsWith('INSERT INTO')).length, 4);
assert.equal(calls.some(({ sql }) => sql === 'COMMIT'), true);
});
@@ -273,15 +266,17 @@ test('returns the original durable identities for an exact replay', async () =>
test('rejects missing, authorization, definition and disabled fences', async () => {
await assert.rejects(
fixture({ projectRows: [] }).repository.startTask(command()),
fixture({
authorizationRows: [{ matches: null }],
projectExistenceRows: [{ exists: false }],
}).repository.startTask(command()),
TaskStartNotFoundError,
);
await assert.rejects(
fixture({ bindingRows: [{
bindingVersion: 4,
bindingState: 'revoked',
bindingRole: null,
}] }).repository.startTask(command()),
fixture({
authorizationRows: [{ matches: false }],
projectExistenceRows: [{ exists: true }],
}).repository.startTask(command()),
(error) =>
error instanceof TaskStartFenceRejectedError &&
error.reason === 'authorization_changed',
@@ -170,6 +170,7 @@ test('accepts timeout only when starting ACK supplied a durable deadline', async
}));
assert.equal(result.status, 'started');
assert.equal(barriers, 1);
await waitForReceipt(roots.receiptRoot);
});
test('propagates unknown outcome when durable identity capture fails after spawn', async (t) => {
@@ -55,6 +55,7 @@ const POSTGRES_ROLE_NAMES = Object.freeze([
'ql3_migration',
'ql3_package_executor',
'ql3_package_manager',
'ql3_run_manager',
'ql3_runtime',
'ql3_worker_credential_executor',
'ql3_worker_credential_manager',
@@ -53,6 +53,7 @@ const ROLE_NAMES = Object.freeze([
'ql3_migration',
'ql3_package_executor',
'ql3_package_manager',
'ql3_run_manager',
'ql3_runtime',
'ql3_worker_credential_executor',
'ql3_worker_credential_manager',
@@ -733,10 +734,14 @@ async function main() {
ql3_migration: randomSecret(),
ql3_runtime: randomSecret(),
ql3_admin: randomSecret(),
ql3_ai_credential_manager: randomSecret(),
ql3_ai_credential_tester: randomSecret(),
ql3_ai_maintenance: randomSecret(),
ql3_automation_manager: randomSecret(),
ql3_approval_manager: randomSecret(),
ql3_package_manager: randomSecret(),
ql3_package_executor: randomSecret(),
ql3_run_manager: randomSecret(),
ql3_worker_credential_manager: randomSecret(),
ql3_worker_credential_executor: randomSecret(),
ql3_worker_ingress: randomSecret(),
+33 -16
View File
@@ -16,6 +16,7 @@ const QL3_VERSION = readReleaseIdentity(DEFAULT_ROOT).version;
const OCI_INDEX_MEDIA_TYPE = 'application/vnd.oci.image.index.v1+json';
const OCI_MANIFEST_MEDIA_TYPE = 'application/vnd.oci.image.manifest.v1+json';
const OCI_CONFIG_MEDIA_TYPE = 'application/vnd.oci.image.config.v1+json';
const OCI_EMPTY_CONFIG_MEDIA_TYPE = 'application/vnd.oci.empty.v1+json';
const OCI_LAYER_MEDIA_TYPE = 'application/vnd.oci.image.layer.v1.tar+gzip';
const IN_TOTO_MEDIA_TYPE = 'application/vnd.in-toto+json';
const SPDX_PREDICATE_TYPE = 'https://spdx.dev/Document';
@@ -447,23 +448,39 @@ function auditAttestation(
OCI_MANIFEST_MEDIA_TYPE,
MAX_INDEX_BYTES,
);
const config = blobReader.readJson(
manifest.config,
OCI_CONFIG_MEDIA_TYPE,
MAX_CONFIG_BYTES,
);
if (
config.architecture !== 'unknown' ||
config.os !== 'unknown' ||
JSON.stringify(config.config) !== JSON.stringify({}) ||
!Array.isArray(config.rootfs?.diff_ids) ||
!Array.isArray(manifest.layers) ||
JSON.stringify(config.rootfs.diff_ids) !==
JSON.stringify(manifest.layers.map((layer) => layer.digest))
) {
throw new Error(
'OCI attestation config must bind an empty unknown platform',
if (manifest.config?.mediaType === OCI_EMPTY_CONFIG_MEDIA_TYPE) {
normalizeDescriptor(
manifest.config,
OCI_EMPTY_CONFIG_MEDIA_TYPE,
MAX_CONFIG_BYTES,
);
const emptyConfig = blobReader.read(
manifest.config,
MAX_CONFIG_BYTES,
true,
);
if (emptyConfig.toString('utf8') !== '{}') {
throw new Error('OCI attestation empty config must be canonical');
}
} else {
const config = blobReader.readJson(
manifest.config,
OCI_CONFIG_MEDIA_TYPE,
MAX_CONFIG_BYTES,
);
if (
config.architecture !== 'unknown' ||
config.os !== 'unknown' ||
JSON.stringify(config.config) !== JSON.stringify({}) ||
!Array.isArray(config.rootfs?.diff_ids) ||
!Array.isArray(manifest.layers) ||
JSON.stringify(config.rootfs.diff_ids) !==
JSON.stringify(manifest.layers.map((layer) => layer.digest))
) {
throw new Error(
'OCI attestation config must bind an empty unknown platform',
);
}
}
if (!Array.isArray(manifest.layers) || manifest.layers.length !== 2) {
throw new Error('OCI attestation must contain exactly SBOM and provenance');
@@ -0,0 +1,119 @@
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const repositoryRoot = path.resolve(__dirname, '..');
const HOST_BOUND_TESTS = Object.freeze({
'packages/ql3-local-owner-cli/test/adoptedDeploymentBundle.test.cjs':
'requires repository-owned deployment entrypoint material',
'packages/ql3-local-owner-cli/test/localDeployment.test.cjs':
'exercises mutable deployment staging and host ownership',
'packages/ql3-local-owner-cli/test/reconciliationCapturePrepare.test.cjs':
'exercises sealed SQLite WAL and SHM capture assets',
'packages/ql3-local-owner-cli/test/serviceBridgeRoot.test.cjs':
'installs systemd and OpenRC descriptors into host service directories',
});
function collectReadonlyOwnerTests(root = repositoryRoot) {
const packagesRoot = path.join(root, 'packages');
const packageNames = fs
.readdirSync(packagesRoot, { withFileTypes: true })
.filter(
(entry) =>
entry.isDirectory() &&
(entry.name === 'ql3-local-command-file' ||
entry.name.startsWith('ql3-local-owner-')),
)
.map(({ name }) => name)
.sort();
const discovered = [];
for (const packageName of packageNames) {
const testRoot = path.join(packagesRoot, packageName, 'test');
if (!fs.existsSync(testRoot)) continue;
for (const entry of fs.readdirSync(testRoot, { withFileTypes: true })) {
if (entry.isFile() && entry.name.endsWith('.test.cjs')) {
discovered.push(
path.posix.join('packages', packageName, 'test', entry.name),
);
}
}
}
discovered.sort();
const missingClassifications = Object.keys(HOST_BOUND_TESTS).filter(
(file) => !discovered.includes(file),
);
if (missingClassifications.length !== 0) {
throw new Error(
`reviewed host-bound Owner tests are missing: ${missingClassifications.join(
', ',
)}`,
);
}
const tests = discovered.filter((file) => !(file in HOST_BOUND_TESTS));
if (tests.length === 0) {
throw new Error('read-only Owner test plan is empty');
}
return Object.freeze({
tests: Object.freeze(tests),
hostBound: HOST_BOUND_TESTS,
});
}
function assertWriteRejected(target) {
try {
fs.writeFileSync(target, 'unexpected-write', { flag: 'wx' });
} catch (error) {
if (error?.code === 'EROFS' || error?.code === 'EACCES') return;
throw error;
}
try {
fs.unlinkSync(target);
} finally {
throw new Error(`read-only boundary accepted a write: ${target}`);
}
}
function assertContainerBoundary(mode) {
if (!['root', 'nonroot'].includes(mode)) {
throw new Error(
'usage: ql3-local-owner-readonly-contract.cjs --mode=root|nonroot',
);
}
const uid = typeof process.getuid === 'function' ? process.getuid() : null;
if (mode === 'root' ? uid !== 0 : uid === null || uid === 0) {
throw new Error(`Owner read-only actor identity is invalid for ${mode}`);
}
const temporaryProbe = fs.mkdtempSync('/tmp/ql3-owner-readonly-');
fs.rmSync(temporaryProbe, { recursive: true });
assertWriteRejected(`/ql3-owner-readonly-root-${process.pid}`);
assertWriteRejected(
path.join(repositoryRoot, `.ql3-owner-readonly-workspace-${process.pid}`),
);
}
function main(argv = process.argv.slice(2)) {
const modeArgument = argv.find((value) => value.startsWith('--mode='));
const mode = modeArgument?.slice('--mode='.length);
assertContainerBoundary(mode);
const plan = collectReadonlyOwnerTests();
const result = spawnSync(
process.execPath,
['--test', '--test-concurrency=1', ...plan.tests],
{ cwd: repositoryRoot, stdio: 'inherit' },
);
if (result.error) throw result.error;
if (result.signal) {
throw new Error(`read-only Owner tests terminated by ${result.signal}`);
}
if (result.status !== 0) process.exitCode = result.status ?? 1;
}
if (require.main === module) main();
module.exports = {
HOST_BOUND_TESTS,
collectReadonlyOwnerTests,
};
@@ -31,6 +31,7 @@ const KIND_NODE_IMAGE =
const POSTGRES_IMAGE = 'postgres:18.4-bookworm';
const POSTGRES_INDEX_DIGEST =
'sha256:1961f96e6029a02c3812d7cb329a3b03a3ac2bb067058dec17b0f5596aca9296';
const POSTGRES_IMAGE_REFERENCE = `${POSTGRES_IMAGE}@${POSTGRES_INDEX_DIGEST}`;
const POSTGRES_REPOSITORY_DIGEST = `postgres@${POSTGRES_INDEX_DIGEST}`;
const DEFAULT_ADMIN_IMAGE = 'qinglong3-cluster-admin:ql3-plugin-recovery-e2e';
const DEFAULT_CONTROL_IMAGE =
@@ -277,13 +278,13 @@ function buildImages(revision) {
}
function ensurePostgresImage() {
if (!imageExists(POSTGRES_IMAGE)) {
run(DOCKER, ['pull', `${POSTGRES_IMAGE}@${POSTGRES_INDEX_DIGEST}`], {
if (!imageExists(POSTGRES_IMAGE_REFERENCE)) {
run(DOCKER, ['pull', POSTGRES_IMAGE_REFERENCE], {
label: 'pull digest-pinned PostgreSQL 18.4 fixture image',
});
}
const inspection = JSON.parse(
run(DOCKER, ['image', 'inspect', POSTGRES_IMAGE], {
run(DOCKER, ['image', 'inspect', POSTGRES_IMAGE_REFERENCE], {
capture: true,
quiet: true,
}).stdout,
@@ -485,7 +486,7 @@ createdb --username "$POSTGRES_USER" --owner ql3_migration qinglong
containers: [
{
name: 'postgres',
image: POSTGRES_IMAGE,
image: POSTGRES_IMAGE_REFERENCE,
imagePullPolicy: 'Never',
env: [
{ name: 'POSTGRES_USER', value: 'postgres' },
@@ -1726,7 +1727,11 @@ async function main(argv = process.argv.slice(2)) {
},
);
created = true;
for (const image of [ADMIN_IMAGE, CONTROL_IMAGE, POSTGRES_IMAGE]) {
for (const image of [
ADMIN_IMAGE,
CONTROL_IMAGE,
POSTGRES_IMAGE_REFERENCE,
]) {
kind(
[
'load',
+1 -1
View File
@@ -117,7 +117,7 @@ function auditPostgresHaEvidence(report) {
'POSTGRES_REPO_DIGESTS_MISSING',
);
add(
postgres.architecture === 'x64' || postgres.architecture === 'arm64',
postgres.architecture === 'amd64' || postgres.architecture === 'arm64',
'POSTGRES_ARCHITECTURE_INVALID',
);
add(
@@ -81,6 +81,7 @@ const ROLE_NAMES = Object.freeze([
'ql3_package_executor',
'ql3_automation_manager',
'ql3_approval_manager',
'ql3_run_manager',
'ql3_worker_credential_manager',
'ql3_worker_credential_executor',
'ql3_worker_ingress',
@@ -47,6 +47,7 @@ const POSTGRES_ROLES = [
'ql3_migration',
'ql3_package_executor',
'ql3_package_manager',
'ql3_run_manager',
'ql3_runtime',
'ql3_worker_credential_executor',
'ql3_worker_credential_manager',
+23 -12
View File
@@ -248,18 +248,20 @@ function createFixture(t, options = {}) {
const attestationLayers = options.omitProvenance
? [spdx]
: [spdx, provenance];
const attestationConfig = blob(
{
architecture: 'unknown',
os: 'unknown',
config: {},
rootfs: {
type: 'layers',
diff_ids: attestationLayers.map((layer) => layer.digest),
},
},
'application/vnd.oci.image.config.v1+json',
);
const attestationConfig = options.modernAttestationConfig
? blob({}, 'application/vnd.oci.empty.v1+json')
: blob(
{
architecture: 'unknown',
os: 'unknown',
config: {},
rootfs: {
type: 'layers',
diff_ids: attestationLayers.map((layer) => layer.digest),
},
},
'application/vnd.oci.image.config.v1+json',
);
const attestationManifest = blob(
{
schemaVersion: 2,
@@ -325,6 +327,15 @@ test('accepts two exact images with bound SBOM and provenance', (t) => {
);
});
test('accepts OCI 1.1 empty attestation configs emitted by current BuildKit', (t) => {
const report = auditClusterOciLayout({
root,
layoutRoot: createFixture(t, { modernAttestationConfig: true }),
expectedRevision: revision,
});
assert.equal(report.platforms.length, 2);
});
test('accepts the independent cluster-admin image and attestation closure', (t) => {
const report = auditClusterOciLayout({
root,
@@ -0,0 +1,45 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { test } = require('node:test');
const {
HOST_BOUND_TESTS,
collectReadonlyOwnerTests,
} = require('../../scripts/ql3-local-owner-readonly-contract.cjs');
const repositoryRoot = path.resolve(__dirname, '../..');
const workflowPath = path.join(repositoryRoot, '.github/workflows/ql3-ci.yml');
test('classifies only reviewed host-bound Owner tests outside the read-only gate', () => {
const plan = collectReadonlyOwnerTests(repositoryRoot);
assert.deepEqual(Object.keys(HOST_BOUND_TESTS).sort(), [
'packages/ql3-local-owner-cli/test/adoptedDeploymentBundle.test.cjs',
'packages/ql3-local-owner-cli/test/localDeployment.test.cjs',
'packages/ql3-local-owner-cli/test/reconciliationCapturePrepare.test.cjs',
'packages/ql3-local-owner-cli/test/serviceBridgeRoot.test.cjs',
]);
assert.equal(plan.tests.length > 0, true);
for (const file of plan.tests) {
assert.equal(fs.existsSync(path.join(repositoryRoot, file)), true);
assert.equal(file in HOST_BOUND_TESTS, false);
}
});
test('runs the reviewed Owner contract as root and non-root in read-only containers', () => {
const workflow = fs.readFileSync(workflowPath, 'utf8');
const job = workflow.match(
/ local-profiles:\n([\s\S]*?)\n cluster-postgres:/,
)?.[1];
assert.ok(job, 'local-profiles job is missing');
assert.equal(job.match(/docker run --rm --read-only/g)?.length, 2);
assert.equal(
job.match(/scripts\/ql3-local-owner-readonly-contract\.cjs/g)?.length,
2,
);
assert.match(job, /--mode=root/);
assert.match(job, /--user 65532:65532[\s\S]*--mode=nonroot/);
assert.doesNotMatch(job, /packages\/ql3-local-owner-\*\/test\/\*\.test\.cjs/);
});
@@ -6,7 +6,7 @@ const {
auditPostgresHaEvidence,
} = require('../../scripts/ql3-postgres-ha-evidence-audit.cjs');
function fixture() {
function fixture(architecture = 'arm64') {
const gates = { passed: true };
for (let index = 0; index < 100; index += 1) gates[`gate${index}`] = true;
return {
@@ -16,7 +16,7 @@ function fixture() {
image: 'postgres:18',
imageId: `sha256:${'a'.repeat(64)}`,
repoDigests: [`postgres@sha256:${'b'.repeat(64)}`],
architecture: 'arm64',
architecture,
version: '18.4',
versionNumber: 180004,
},
@@ -85,6 +85,7 @@ test('accepts complete PostgreSQL HA evidence', () => {
compatible: true,
findings: [],
});
assert.equal(auditPostgresHaEvidence(fixture('amd64')).compatible, true);
});
test('rejects false gates, promotion drift and hidden private material', () => {
@@ -338,6 +338,7 @@ function publisherProvenanceInstallRepository(repository, provenance) {
findLock: (...args) => repository.findLock(...args),
create: (...args) => repository.create(...args),
listRecoveryPage: (...args) => repository.listRecoveryPage(...args),
listCurrentPage: (...args) => repository.listCurrentPage(...args),
async commit(command) {
if (command.record.state !== 'staged') {
return repository.commit(command);