Copilot pipeline fix v3

This commit is contained in:
Josh 2025-08-09 18:54:18 +00:00
parent a1d759167d
commit 77ac250646
2 changed files with 26 additions and 30 deletions

View File

@ -115,7 +115,7 @@ steps:
gcloud secrets versions access latest --secret=WRAPPED_DEK_dev --project=$PROJECT > /tmp/dev_dek.enc; \ gcloud secrets versions access latest --secret=WRAPPED_DEK_dev --project=$PROJECT > /tmp/dev_dek.enc; \
if [ -s /tmp/dev_dek.enc ]; then \ if [ -s /tmp/dev_dek.enc ]; then \
docker volume ls -q | grep -qx aptiva_dek_staging || docker volume create aptiva_dek_staging >/dev/null; \ docker volume ls -q | grep -qx aptiva_dek_staging || docker volume create aptiva_dek_staging >/dev/null; \
sudo docker run --rm -v aptiva_dek_staging:/v -v /tmp:/host busybox sh -c 'set -e; mkdir -p /v/staging; cp -f /host/dev_dek.enc /v/staging/dek.enc; chown 1000:1000 /v/staging/dek.enc; chmod 400 /v/staging/dek.enc; rm -f /v/staging/dek.fpr; echo -n "staging dek.enc bytes: "; wc -c </v/staging/dek.enc; ls -l /v/staging' sudo docker run --rm -v aptiva_dek_staging:/v -v /tmp:/host busybox sh -c 'set -e; mkdir -p /v/staging; cp -f /host/dev_dek.enc /v/staging/dek.enc; chown 1000:1000 /v/staging/dek.enc; chmod 400 /v/staging/dek.enc; rm -f /v/staging/dek.fpr; echo -n \"staging dek.enc bytes: \"; wc -c </v/staging/dek.enc; ls -l /v/staging'
else \ else \
echo \"⚠️ WRAPPED_DEK_dev returned empty; skipping copy\"; \ echo \"⚠️ WRAPPED_DEK_dev returned empty; skipping copy\"; \
fi; \ fi; \

View File

@ -4,15 +4,11 @@ import helmet from 'helmet';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import path from 'path'; import path from 'path';
import bodyParser from 'body-parser';
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken'; // For token-based authentication import jwt from 'jsonwebtoken';
import { initEncryption, encrypt, decrypt, verifyCanary, SENTINEL } from './shared/crypto/encryption.js'; import { initEncryption, encrypt, decrypt, verifyCanary, SENTINEL } from './shared/crypto/encryption.js';
import pool from './config/mysqlPool.js';
import pool from './config/mysqlPool.js'; // adjust path if needed // import sqlite3 from 'sqlite3'; // (unused here safe to remove)
import sqlite3 from 'sqlite3';
const CANARY_SQL = ` const CANARY_SQL = `
CREATE TABLE IF NOT EXISTS encryption_canary ( CREATE TABLE IF NOT EXISTS encryption_canary (
@ -22,13 +18,11 @@ const CANARY_SQL = `
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
const rootPath = path.resolve(__dirname, '..');
const rootPath = path.resolve(__dirname, '..'); // Up one level const env = process.env.NODE_ENV?.trim() || 'development';
const env = process.env.NODE_ENV?.trim() || 'development';
const envPath = path.resolve(rootPath, `.env.${env}`); const envPath = path.resolve(rootPath, `.env.${env}`);
dotenv.config({ path: envPath }); // Load .env file dotenv.config({ path: envPath, override: false });
// Grab secrets and config from ENV
const { const {
JWT_SECRET, JWT_SECRET,
CORS_ALLOWED_ORIGINS, CORS_ALLOWED_ORIGINS,
@ -37,46 +31,48 @@ const {
if (!JWT_SECRET) { if (!JWT_SECRET) {
console.error('FATAL: JWT_SECRET missing aborting startup'); console.error('FATAL: JWT_SECRET missing aborting startup');
process.exit(1); // container exits, Docker marks it unhealthy process.exit(1);
} }
if (!CORS_ALLOWED_ORIGINS) { if (!CORS_ALLOWED_ORIGINS) {
console.error('FATAL: CORS_ALLOWED_ORIGINS missing aborting startup'); console.error('FATAL: CORS_ALLOWED_ORIGINS missing aborting startup');
process.exit(1); process.exit(1);
} }
/* ─── unwrap / verify DEK before we serve requests ────────────── */ // Unwrap / verify DEK and seed canary before serving traffic
await initEncryption();
try { try {
/* quick connectivity smoketest (optional) */ await initEncryption(); // <-- wrap in try/catch
await pool.query('SELECT 1');
/* ① ensure table exists */ const db = pool.raw || pool; // <-- bypass DAO wrapper for canary ops
await pool.query(CANARY_SQL);
/* ② insert sentinel on first run */ // quick connectivity check
await pool.query( await db.query('SELECT 1');
// ① ensure table
await db.query(CANARY_SQL);
// ② insert sentinel on first run (ignore if exists)
await db.query(
'INSERT IGNORE INTO encryption_canary (id, value) VALUES (1, ?)', 'INSERT IGNORE INTO encryption_canary (id, value) VALUES (1, ?)',
[encrypt(SENTINEL)] [encrypt(SENTINEL)]
); );
/* ③ read back & verify */ // ③ read back & verify
const [rows] = await pool.query( const [rows] = await db.query(
'SELECT value FROM encryption_canary WHERE id = 1 LIMIT 1' 'SELECT value FROM encryption_canary WHERE id = 1 LIMIT 1'
); );
const plaintext = decrypt(rows[0]?.value || ''); const plaintext = decrypt(rows[0]?.value || '');
if (plaintext !== SENTINEL) { if (plaintext !== SENTINEL) {
throw new Error('DEK mismatch with database sentinel'); throw new Error('DEK mismatch with database sentinel');
} }
console.log('[ENCRYPT] DEK verified against canary  proceeding'); console.log('[ENCRYPT] DEK verified against canary proceeding');
} catch (err) { } catch (err) {
console.error('FATAL:', err.message || err); console.error('FATAL:', err?.message || err);
process.exit(1); // container restarts → alert process.exit(1);
} }
// …the rest of your server: app = express(), middlewares, routes, app.listen()
/* /*
Express app & middleware Express app & middleware
---------------------------------------------------------------- */ ---------------------------------------------------------------- */