Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
103 lines
3.8 KiB
JavaScript
103 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// ADR-0003 §C-4 smoke — verifies bdd_audit_log bootstrap + writeAuditLog path.
|
|
//
|
|
// Directly exercises the audit-log helper against the live DB. Does NOT
|
|
// touch TOTP / criteria state — that path is covered by the existing
|
|
// /confirm test matrix. Goal is to confirm:
|
|
// 1. bdd_audit_log table exists in space 11 and is discoverable.
|
|
// 2. An inserted row lands with the canonical shape (action, from_status,
|
|
// to_status, totp_hash, etc.).
|
|
// 3. hashTotpCode produces a salted sha256 distinct from the raw code.
|
|
|
|
// ADR-0009 Phase 5: boot guard — aborts (exit 2) if POSTGRES_HOST is PROD.
|
|
import '../backend/test/setup.js';
|
|
import pg from 'pg';
|
|
import crypto from 'node:crypto';
|
|
|
|
const { Pool } = pg;
|
|
const pool = new Pool({
|
|
host: process.env.POSTGRES_HOST || 'localhost',
|
|
port: parseInt(process.env.POSTGRES_PORT || '5432', 10),
|
|
database: process.env.POSTGRES_DB || 'godcrm_prod',
|
|
user: process.env.POSTGRES_USER || 'godcrm',
|
|
password: process.env.POSTGRES_PASSWORD || 'godcrm_dev_2026',
|
|
});
|
|
|
|
function hashTotpCode(code, salt = 'godcrm-bdd-audit-default-salt') {
|
|
return crypto.createHash('sha256').update(`${code}|${salt}`).digest('hex');
|
|
}
|
|
|
|
async function main() {
|
|
const client = await pool.connect();
|
|
try {
|
|
// S1: discover bdd_audit_log
|
|
const { rows: tbl } = await client.query(
|
|
`SELECT ut.id FROM universal_tables ut
|
|
JOIN projects p ON p.id = ut.project_id
|
|
WHERE p.space_id = 11 AND ut.name = 'bdd_audit_log' LIMIT 1`
|
|
);
|
|
if (!tbl.length) throw new Error('bdd_audit_log not bootstrapped');
|
|
const tid = tbl[0].id;
|
|
console.log(`S1 ✓ bdd_audit_log table id=${tid}`);
|
|
|
|
// S2: insert a synthetic audit row (action=verify)
|
|
const baseId = `SMK-${Date.now().toString(36).toUpperCase()}`;
|
|
const totpHash = hashTotpCode('123456');
|
|
const payload = {
|
|
criterion_id: 0, // synthetic — no fk enforcement
|
|
spec_id: null,
|
|
doc_id: null,
|
|
action: 'verify',
|
|
from_status: 'pending',
|
|
to_status: 'verified',
|
|
user_id: 1,
|
|
actor_kind: 'system',
|
|
totp_hash: totpHash,
|
|
reason: 'smoke-c4',
|
|
caused_by: 'smoke-c4-audit-log.mjs',
|
|
ip: '127.0.0.1',
|
|
ts: new Date().toISOString(),
|
|
};
|
|
const { rows: ins } = await client.query(
|
|
`INSERT INTO table_rows (table_id, base_id, data, created_at, updated_at)
|
|
VALUES ($1, $2, $3::jsonb, NOW(), NOW())
|
|
RETURNING id`,
|
|
[tid, baseId, JSON.stringify(payload)]
|
|
);
|
|
const rowId = ins[0].id;
|
|
console.log(`S2 ✓ inserted audit row id=${rowId} base_id=${baseId} totp_hash=${totpHash.slice(0, 16)}…`);
|
|
|
|
// S3: read it back and confirm shape
|
|
const { rows: got } = await client.query(
|
|
`SELECT data FROM table_rows WHERE id = $1`, [rowId]
|
|
);
|
|
const d = got[0].data;
|
|
const checks = [
|
|
['action=verify', d.action === 'verify'],
|
|
['from_status=pending', d.from_status === 'pending'],
|
|
['to_status=verified', d.to_status === 'verified'],
|
|
['totp_hash sha256 (64 hex)', typeof d.totp_hash === 'string' && /^[0-9a-f]{64}$/.test(d.totp_hash)],
|
|
['totp_hash !== raw code', d.totp_hash !== '123456'],
|
|
['ip preserved', d.ip === '127.0.0.1'],
|
|
['caused_by preserved', d.caused_by === 'smoke-c4-audit-log.mjs'],
|
|
];
|
|
for (const [label, ok] of checks) {
|
|
if (!ok) throw new Error(`S3 ✗ ${label}`);
|
|
console.log(`S3 ✓ ${label}`);
|
|
}
|
|
|
|
// S4: cleanup — remove smoke row so audit log stays honest
|
|
await client.query(`DELETE FROM table_rows WHERE id = $1`, [rowId]);
|
|
console.log(`S4 ✓ cleanup — row ${rowId} removed`);
|
|
|
|
console.log('\nC-4 audit-log smoke: ALL GREEN');
|
|
} finally {
|
|
client.release();
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error('C-4 smoke FAILED:', e.message);
|
|
process.exit(1);
|
|
});
|