CI (SIAX Cloud) / security (pull_request) Successful in 14s
CI (SIAX Cloud) / security (push) Successful in 14s
CI (SIAX Cloud) / contracts (pull_request) Successful in 15s
CI (SIAX Cloud) / contracts (push) Successful in 17s
CI (SIAX Cloud) / quality (push) Successful in 1m9s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m10s
- scan worker: atomic claim (FOR UPDATE SKIP LOCKED), Playwright-core + system chromium capture → typed EvidenceRecords (runtime-html/dom/computed-style/ stylesheet/asset/network-request/screenshot), confidence=measured - AUD0 emitter (fire-and-forget, advisory): c0py.registry_created/.scan_created/ .scan_completed/.scan_failed, tenant_id = Zitadel resourceowner (org) - tenant binding: resourceowner claim from introspection (deny-by-default 403), migration 002 tenant_id on registries+scans, all queries tenant+owner scoped - evidence gaps stay explicit (screenshot miss → no screenshot record) - 30/30 tests, canonical validator OK
59 lines
1.7 KiB
TypeScript
59 lines
1.7 KiB
TypeScript
export interface RegistryRow {
|
|
id: string;
|
|
owner_sub: string;
|
|
tenant_id: string;
|
|
name: string;
|
|
url: string;
|
|
config: unknown;
|
|
created_at: Date;
|
|
}
|
|
|
|
export interface PoolLike {
|
|
query(text: string, values?: unknown[]): Promise<{ rows: Record<string, unknown>[] }>;
|
|
}
|
|
|
|
function toRegistry(row: Record<string, unknown>): {
|
|
id: string;
|
|
name: string;
|
|
url: string;
|
|
config: unknown;
|
|
createdAt: string;
|
|
} {
|
|
return {
|
|
id: String(row.id),
|
|
name: String(row.name),
|
|
url: String(row.url),
|
|
config: row.config,
|
|
createdAt: new Date(row.created_at as string).toISOString(),
|
|
};
|
|
}
|
|
|
|
export async function listRegistries(db: PoolLike, ownerSub: string, tenantId: string) {
|
|
const res = await db.query(
|
|
"SELECT id, owner_sub, tenant_id, name, url, config, created_at FROM registries WHERE owner_sub = $1 AND tenant_id = $2 ORDER BY created_at DESC",
|
|
[ownerSub, tenantId],
|
|
);
|
|
return res.rows.map(toRegistry);
|
|
}
|
|
|
|
export async function createRegistry(
|
|
db: PoolLike,
|
|
ownerSub: string,
|
|
tenantId: string,
|
|
input: { name: string; url: string; config?: unknown },
|
|
) {
|
|
const res = await db.query(
|
|
"INSERT INTO registries (owner_sub, tenant_id, name, url, config) VALUES ($1, $2, $3, $4, $5::jsonb) RETURNING id, owner_sub, tenant_id, name, url, config, created_at",
|
|
[ownerSub, tenantId, input.name, input.url, JSON.stringify(input.config ?? {})],
|
|
);
|
|
return toRegistry(res.rows[0]);
|
|
}
|
|
|
|
export async function getRegistry(db: PoolLike, ownerSub: string, tenantId: string, id: string) {
|
|
const res = await db.query(
|
|
"SELECT id, owner_sub, tenant_id, name, url, config, created_at FROM registries WHERE owner_sub = $1 AND tenant_id = $2 AND id = $3",
|
|
[ownerSub, tenantId, id],
|
|
);
|
|
return res.rows[0] ? toRegistry(res.rows[0]) : null;
|
|
}
|