feat(deploy): add Contabo release workflow and portal payload gate
This commit is contained in:
1 parent
5fa54719b2
commit
ba93a54942
18 files changed
+697
-28
No files matched your search
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user