{"record":{"id":"e4b779f6ed0047c1","repo":"oven-sh/bun","slug":"permitted-violation","errorCode":"PERMITTED_VIOLATION","errorMessage":"PERMITTED_VIOLATION","messagePattern":"PERMITTED_VIOLATION","errorType":"error_code","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/http/error.rs","lineNumber":215,"sourceCode":"    #[error(\"PROXY_PATH_LENGTH_EXCEEDED\")]\n    PROXY_PATH_LENGTH_EXCEEDED,\n    #[error(\"KEYUSAGE_NO_DIGITAL_SIGNATURE\")]\n    KEYUSAGE_NO_DIGITAL_SIGNATURE,\n    #[error(\"PROXY_CERTIFICATES_NOT_ALLOWED\")]\n    PROXY_CERTIFICATES_NOT_ALLOWED,\n    #[error(\"INVALID_EXTENSION\")]\n    INVALID_EXTENSION,\n    #[error(\"INVALID_POLICY_EXTENSION\")]\n    INVALID_POLICY_EXTENSION,\n    #[error(\"NO_EXPLICIT_POLICY\")]\n    NO_EXPLICIT_POLICY,\n    #[error(\"DIFFERENT_CRL_SCOPE\")]\n    DIFFERENT_CRL_SCOPE,\n    #[error(\"UNSUPPORTED_EXTENSION_FEATURE\")]\n    UNSUPPORTED_EXTENSION_FEATURE,\n    #[error(\"UNNESTED_RESOURCE\")]\n    UNNESTED_RESOURCE,\n    #[error(\"PERMITTED_VIOLATION\")]\n    PERMITTED_VIOLATION,\n    #[error(\"EXCLUDED_VIOLATION\")]\n    EXCLUDED_VIOLATION,\n    #[error(\"SUBTREE_MINMAX\")]\n    SUBTREE_MINMAX,\n    #[error(\"APPLICATION_VERIFICATION\")]\n    APPLICATION_VERIFICATION,\n    #[error(\"UNSUPPORTED_CONSTRAINT_TYPE\")]\n    UNSUPPORTED_CONSTRAINT_TYPE,\n    #[error(\"UNSUPPORTED_CONSTRAINT_SYNTAX\")]\n    UNSUPPORTED_CONSTRAINT_SYNTAX,\n    #[error(\"UNSUPPORTED_NAME_SYNTAX\")]\n    UNSUPPORTED_NAME_SYNTAX,\n    #[error(\"CRL_PATH_VALIDATION_ERROR\")]\n    CRL_PATH_VALIDATION_ERROR,\n    #[error(\"SUITE_B_INVALID_VERSION\")]\n    SUITE_B_INVALID_VERSION,\n    #[error(\"SUITE_B_INVALID_ALGORITHM\")]","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/src/http/error.rs#L197-L233","documentation":"CertError::PERMITTED_VIOLATION maps BoringSSL X509_V_ERR_PERMITTED_VIOLATION (verify code 47). A CA in the chain carries a nameConstraints extension with permittedSubtrees, and the name being validated (SAN/subject of a lower cert) falls outside every permitted subtree. Message: \"permitted subtree violation\".","triggerScenarios":"CA has nameConstraints permittedSubtrees = DNS:example.com, but a cert under it was issued with SAN www.other.org (or an IP/email/URI outside the permitted list). Any name present in the cert that is constrained must match a permitted subtree.","commonSituations":"Internal CAs constrained to corporate domains asked to sign certs for a new/acquired domain; CA templates copied between unconstrained and constrained hierarchies; mixing an enterprise intermediate under a constrained public root.","solutions":["Print the constraints: openssl x509 -in ca.pem -noout -text and read the Name Constraints permitted subtrees","Reissue the leaf with only names inside the permitted subtrees (drop the offending SAN)","Or widen the CA's permittedSubtrees to include the name and reissue the CA chain","Verify the chain locally: openssl verify -CAfile constrained-ca.pem leaf.pem"],"exampleFix":"# before: CA permits DNS:corp.example.com ; leaf requests a foreign SAN\n[leaf_ext]\nsubjectAltName = DNS:api.corp.example.com, DNS:api.partner.org\n\n# after: SANs within the permitted subtree\n[leaf_ext]\nsubjectAltName = DNS:api.corp.example.com","handlingStrategy":"validation","validationCode":"// Before issuing/before trusting: check the SAN sits inside the CA's permitted subtrees\nimport { X509Certificate } from \"node:crypto\";\nexport function sanWithinPermitted(leafPem, caPem, permitted /* e.g. [\"corp.example.com\"] */) {\n  const leaf = new X509Certificate(leafPem);\n  const ca = new X509Certificate(caPem);\n  if (!/(Name Constraints|permittedSubtrees)/i.test(ca.toString())) return true; // unconstrained\n  const dns = (leaf.subjectAltName ?? \"\").match(/DNS:([^,\\s]+)/g)?.map((s) => s.slice(4)) ?? [];\n  return dns.every((name) => permitted.some((p) => name === p || name.endsWith(\".\" + p)));\n}","typeGuard":"export function isPermittedViolation(e): e is Error & { code: \"PERMITTED_VIOLATION\" } {\n  return e instanceof Error && (e as any).code === \"PERMITTED_VIOLATION\";\n}","tryCatchPattern":"try {\n  await fetch(url);\n} catch (e) {\n  if (e?.code === \"PERMITTED_VIOLATION\") {\n    // SAN is outside the CA's permittedSubtrees - reissue leaf or widen the CA\n    reportToCaOwner(new URL(url).hostname);\n  } else throw e;\n}","preventionTips":["Encode the CA's permitted subtrees as data in your issuance pipeline and assert every SAN against them pre-signing","When acquiring domains, update nameConstraints before issuing certs for the new names","Prefer unconstrained public CAs for public names; use constrained CAs only where the policy is enforced end-to-end"],"tags":["tls","x509","name-constraints","san","pki","fetch","security"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}