godcrm/scripts/smoke-c4-audit-log.mjs
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

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);
});