[grade=B] api: fix undeclared 'format' runtime bug in routes/ca.js + 10 lint errors

- routes/ca.js: declare format before pfx/pem/crt dispatch (was ReferenceError
  on every request that passed validation); add CA_CERT_FORMATS single source
  of truth + hardened format extraction (string-coerce, whitelist)
- routes/caddycode.js: fix upstream-validation regexes (control-char classes)
- routes/logs.js: SSE/validation lint fixes
- routes/openclaw.js: remove useless regex escape in ALLOWED_PATH_RE
- fleet-validation.js + http-caddy-admin-origin test: eslint-disable for
  intentional control-regex security sentinels
- ca-dc076.routes.test.js: regression test for declared format + behavioral
  coverage of format validation (now pre-PKI)

Judge: Qwen lane (qwen3.8-max) grade B, 0 blocking, verdict
/tmp/judge-batch1-verdict.json. API suite 2859/2859 green.
This commit is contained in:
Hermes
2026-08-31 23:37:00 -07:00
parent b6678cf591
commit c28322eb46
9 changed files with 92 additions and 11 deletions
@@ -262,6 +262,67 @@ describe('DC-076: CA cert/key disclosure hardening', () => {
});
});
describe('regression: `format` is declared before the dispatch block', () => {
// The handler referenced `format` five times in the pfx/pem/crt/key/
// fullchain dispatch without ever declaring it — every request that
// reached that far threw ReferenceError. The behavioral tests above
// can't reach the dispatch (PKI files absent in the test env returns
// 500 first), so pin the declaration at the source level instead.
test('routes/ca.js declares `format` before dispatch', () => {
const src = fs.readFileSync(
path.join(__dirname, '../../routes/ca.js'), 'utf8');
// Declaration derives from req.query.format (via rawFormat) and the
// canonical format list drives validation.
expect(src).toMatch(/const\s+rawFormat\s*=\s*req\.query\.format/);
expect(src).toMatch(/const\s+format\s*=\s*rawFormat\s*\|\|\s*'pfx'/);
expect(src).toMatch(/CA_CERT_FORMATS\s*=\s*\[.*'pfx'.*'fullchain'.*\]/s);
// And the declaration must come before the first dispatch use.
const declIdx = src.search(/const\s+format\s*=/);
const useIdx = src.indexOf("if (format === 'pfx')");
expect(declIdx).toBeGreaterThanOrEqual(0);
expect(useIdx).toBeGreaterThan(declIdx);
});
});
describe('/cert/:domain — format validation (DC-076_FORMAT_INVALID)', () => {
// These validations run before the PKI file check, so they are
// reachable in the test environment (unlike the dispatch itself).
test('rejects unknown format value', async () => {
const { app } = createCaApp({ scope: ['admin'] });
const res = await request(app)
.get('/ca/cert/dns1.local?format=garbage');
expect(res.status).toBe(400);
expect(res.body.code).toBe('DC-076_FORMAT_INVALID');
});
test('rejects empty format value', async () => {
const { app } = createCaApp({ scope: ['admin'] });
const res = await request(app)
.get('/ca/cert/dns1.local?format=');
expect(res.status).toBe(400);
expect(res.body.code).toBe('DC-076_FORMAT_INVALID');
});
test('rejects array format (?format=a&format=b)', async () => {
const { app } = createCaApp({ scope: ['admin'] });
const res = await request(app)
.get('/ca/cert/dns1.local?format=pem&format=crt');
expect(res.status).toBe(400);
expect(res.body.code).toBe('DC-076_FORMAT_INVALID');
});
test('accepts every documented format (validation passes; PKI 500 is fine)', async () => {
for (const fmt of ['pfx', 'pem', 'crt', 'key', 'fullchain']) {
const { app } = createCaApp({ scope: ['admin'] });
const qs = fmt === 'pfx' ? `format=pfx&password=GoodPass12` : `format=${fmt}`;
const res = await request(app).get(`/ca/cert/dns1.local?${qs}`);
// Must NOT be a format rejection — anything else (e.g. 500 CA not
// found in the test env) proves validation accepted the format.
expect(res.body.code).not.toBe('DC-076_FORMAT_INVALID');
}
});
});
describe('/cert/:domain — domain validation', () => {
test('rejects single-label domain (no dot)', async () => {
const { app } = createCaApp({ scope: ['admin'] });
@@ -53,6 +53,7 @@ function stripComments(src) {
.replace(/\/\*[\s\S]*?\*\//g, '') // block comments
.replace(/(^|[^:])\/\/.*$/gm, '$1'); // line comments, leaving URLs alone
// Pass 3: restore template literals.
// eslint-disable-next-line no-control-regex -- \u0000 is the sentinel from pass 1
return protectedSrc.replace(/\u0000TPL(\d+)\u0000/g, (_, idx) => templates[+idx]);
}