fix(federation/auth-guard): remediate CRIT-1/CRIT-2 + HIGH-1..4 review findings
- CRIT-1: Validate cert subjectUserId against grant.subjectUserId from DB; use authoritative DB value in FederationContext - CRIT-2: Add @Inject(GrantsService) decorator (tsx/esbuild requirement) - HIGH-1: Validate UTF8String TLV tag, length, and bounds in OID parser - HIGH-2: Collapse all 403 wire messages to a generic string to prevent grant enumeration; keep internal logger detail - HIGH-3: Assert federation wire envelope shape in all guard tests - HIGH-4: Regression test for subjectUserId cert/DB mismatch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -50,10 +50,40 @@ const decoder = new TextDecoder();
|
||||
/**
|
||||
* Decode an extension value encoded as ASN.1 UTF8String TLV
|
||||
* (tag 0x0C + 1-byte length + UTF-8 bytes).
|
||||
* Slices off the 2-byte TLV header and decodes the remainder as UTF-8.
|
||||
* Validates tag, length byte, and buffer bounds before decoding.
|
||||
* Throws a descriptive Error on malformed input; caller wraps in try/catch.
|
||||
*/
|
||||
function decodeUtf8StringTlv(value: ArrayBuffer): string {
|
||||
const bytes = new Uint8Array(value);
|
||||
|
||||
// Need at least tag + length bytes
|
||||
if (bytes.length < 2) {
|
||||
throw new Error(`UTF8String TLV too short: expected at least 2 bytes, got ${bytes.length}`);
|
||||
}
|
||||
|
||||
// Tag byte must be 0x0C (ASN.1 UTF8String)
|
||||
if (bytes[0] !== 0x0c) {
|
||||
throw new Error(
|
||||
`UTF8String TLV tag mismatch: expected 0x0C, got 0x${bytes[0]!.toString(16).toUpperCase()}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Only single-byte length form is supported (values 0–127); long form not needed
|
||||
// for OID strings of this length.
|
||||
const declaredLength = bytes[1]!;
|
||||
if (declaredLength > 127) {
|
||||
throw new Error(
|
||||
`UTF8String TLV uses long-form length (0x${declaredLength.toString(16).toUpperCase()}), which is not supported`,
|
||||
);
|
||||
}
|
||||
|
||||
// Declared length must match actual remaining bytes
|
||||
if (declaredLength !== bytes.length - 2) {
|
||||
throw new Error(
|
||||
`UTF8String TLV length mismatch: declared ${declaredLength}, actual ${bytes.length - 2}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Skip: tag (1 byte) + length (1 byte)
|
||||
return decoder.decode(bytes.slice(2));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user