fix(voyage): permanently block cloud metadata endpoints in OTLP validator (CWE-918)

Found by simulert v4.1 smoke — doc/code-drift in v4.1 ship:
docs/observability.md claims "Cloud metadata endpoints (169.254.169.254)
are permanently blocked" but the validator allowed them when
VOYAGE_OTEL_ALLOW_PRIVATE=1. Cloud metadata services expose IAM
credentials and instance secrets — operator-trust extended to
RFC-1918 home-lab access does NOT extend here, because the
blast-radius (cloud-account compromise) is qualitatively different.

New HARD_BLOCKED_HOSTS set checked BEFORE the link-local opt-in path:
  - 169.254.169.254  (AWS / GCP / Azure metadata)
  - 100.100.100.200  (AliCloud metadata)
  - metadata.google.internal
  - metadata.azure.com

New error code ENDPOINT_HARD_BLOCKED. Existing test for
ENDPOINT_LINK_LOCAL_REJECTED on 169.254.169.254 updated to assert
the new code; 3 new tests verify the hard-block holds even with
VOYAGE_OTEL_ALLOW_PRIVATE=1, plus AliCloud + GCP-hostname coverage.

Tests: 487 → 490 pass + 2 skipped.
This commit is contained in:
Kjell Tore Guttormsen 2026-05-09 10:23:51 +02:00
commit 8dc3090080
2 changed files with 48 additions and 3 deletions

View file

@ -74,10 +74,34 @@ test('path-validator: FORBIDDEN_PREFIXES exports drift-pin', () => {
// ---- endpoint-validator: CWE-918 mitigation -------------------------------
test('endpoint-validator: rejects http://169.254.169.254/ link-local (cloud metadata, CWE-918)', () => {
test('endpoint-validator: rejects http://169.254.169.254/ — PERMANENTLY blocked (CWE-918 cloud metadata)', () => {
const r = validateOtlpEndpoint('http://169.254.169.254/v1/metrics', { env: {} });
assert.equal(r.valid, false);
assert.ok(r.errors.find(e => e.code === 'ENDPOINT_LINK_LOCAL_REJECTED'));
assert.ok(r.errors.find(e => e.code === 'ENDPOINT_HARD_BLOCKED'));
});
test('endpoint-validator: 169.254.169.254 stays blocked EVEN WITH VOYAGE_OTEL_ALLOW_PRIVATE=1', () => {
// Cloud metadata service is qualitatively different from RFC-1918 home-lab
// access — operator-trust is NOT extended here. AWS/GCP/Azure metadata
// exposes IAM credentials and can compromise the entire cloud account.
const r = validateOtlpEndpoint('http://169.254.169.254/v1/metrics',
{ env: { VOYAGE_OTEL_ALLOW_PRIVATE: '1' } });
assert.equal(r.valid, false, 'cloud metadata MUST stay blocked even with opt-in');
assert.ok(r.errors.find(e => e.code === 'ENDPOINT_HARD_BLOCKED'));
});
test('endpoint-validator: AliCloud metadata 100.100.100.200 PERMANENTLY blocked', () => {
const r = validateOtlpEndpoint('http://100.100.100.200/latest/meta-data',
{ env: { VOYAGE_OTEL_ALLOW_PRIVATE: '1' } });
assert.equal(r.valid, false);
assert.ok(r.errors.find(e => e.code === 'ENDPOINT_HARD_BLOCKED'));
});
test('endpoint-validator: metadata.google.internal hostname PERMANENTLY blocked', () => {
const r = validateOtlpEndpoint('http://metadata.google.internal/computeMetadata/v1',
{ env: { VOYAGE_OTEL_ALLOW_PRIVATE: '1' } });
assert.equal(r.valid, false);
assert.ok(r.errors.find(e => e.code === 'ENDPOINT_HARD_BLOCKED'));
});
test('endpoint-validator: rejects http://example.com/ (requires https)', () => {