feat(deploy): add Contabo release workflow and portal payload gate

This commit is contained in:
DeCentN2Madness authored and AIAMarketing committed 2026-11-05 00:40:18 -07:00
1 parent 5fa54719b2
commit ba93a54942
18 files changed
+697 -28

No files matched your search

+23 -1
View File
@@ -4,6 +4,8 @@ import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import { verifyArtifact } from './phase10-artifact-verify.mjs';
import { createHash } from 'node:crypto';
import { encryptPortalStream, portalAlgorithm } from '../src/utils/portal-crypto.mjs';
async function fixture(mode = 'staging', forms = 'live') {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'aia-phase10-artifact-'));
@@ -139,10 +141,30 @@ test('release verification rejects private portal resources and encrypted payloa
await fs.appendFile(path.join(root, 'index.html'), '<script type="application/json" data-portal-payload>{"streams":[]}</script>');
const result = await verifyArtifact(root, 'staging');
assert.equal(result.passed, false);
assert.ok(result.errors.some((error) => error.includes('private portal ciphertext')));
assert.ok(result.errors.some((error) => error.includes('portal ciphertext')));
assert.ok(result.errors.some((error) => error.includes('private/legacy')));
});
test('approved production portal accepts only the expected two-stream ciphertext on the noindex route', async () => {
const root = await fixture('indexing');
const payload = { alg: portalAlgorithm, streams: [
await encryptPortalStream('staff', '<p>Synthetic staff</p>', 'synthetic-staff-code'),
await encryptPortalStream('management', '<p>Synthetic management</p>', 'synthetic-management-code'),
] };
const portal = path.join(root, 'employee-portal/index.html');
await fs.mkdir(path.dirname(portal), { recursive: true });
await fs.writeFile(portal, '<!doctype html><html><head><title>Portal</title><meta name="robots" content="noindex,follow"><link rel="canonical" href="https://www.azinstitute4autism.com/employee-portal"></head><body><script data-portal-payload type="application/json">' + JSON.stringify(payload) + '</script></body></html>');
const sha256 = createHash('sha256').update(JSON.stringify(payload)).digest('hex');
const result = await verifyArtifact(root, 'indexing', 'live', { mode: 'production', sha256 });
assert.equal(result.passed, true, result.errors.join('; '));
await fs.appendFile(portal, '<!-- changed -->');
assert.equal((await verifyArtifact(root, 'indexing', 'live', { mode: 'production', sha256 })).passed, true);
const corrupted = JSON.parse(JSON.stringify(payload));
corrupted.streams[0].ct = (corrupted.streams[0].ct[0] === 'A' ? 'B' : 'A') + corrupted.streams[0].ct.slice(1);
await fs.writeFile(portal, '<!doctype html><html><head><title>Portal</title><meta name="robots" content="noindex,follow"><link rel="canonical" href="https://www.azinstitute4autism.com/employee-portal"></head><body><script data-portal-payload type="application/json">' + JSON.stringify(corrupted) + '</script></body></html>');
assert.equal((await verifyArtifact(root, 'indexing', 'live', { mode: 'production', sha256 })).passed, false);
});
test('artifact rejects extra routes, wrong origins and prematurely loaded drawers', async () => {
const root = await fixture();