{
  "schema_version": "1.0.0",
  "record_type": "citation-evidence-pack",
  "pack_id": "saml-decoder",
  "title": "SAML routing and mismatch evidence",
  "description": "Deterministic SAML outcomes for a matching response, a destination/recipient conflict, and malformed HTTP-POST Base64.",
  "scope": "These cases prove decoding and diagnostic observations. Signature markers are not cryptographic signature verification or trust.",
  "canonical_url": "https://encrypt-online.com/evidence/saml-decoder.json",
  "tool": {
    "id": "saml-decoder",
    "name": "SAML Decoder and Diagnostics",
    "path": "/tools/saml-decoder",
    "canonical_url": "https://encrypt-online.com/tools/saml-decoder"
  },
  "implementation": {
    "version": "source-sha256:c22e71a6efa9d83c",
    "aggregate_sha256": "c22e71a6efa9d83ca8ff38010f735345d8849a4bc7869ddce531def133d21c95",
    "sources": [
      {
        "path": "lib/saml-diagnostics-core.mjs",
        "sha256": "dd6a28e923ca0ec34a35e9a68a0e41852bef9f9d594baa2cbc9a171288b9189f"
      },
      {
        "path": "lib/saml-diagnostics-fixtures.mjs",
        "sha256": "e2ac25bfbb9aafd43effd3ae35a8f0a484097609f2d070eaeeac284aa0131579"
      }
    ]
  },
  "verification": {
    "last_verified": "2026-08-05",
    "command": "npm run check:citation-evidence",
    "environments": [
      "Node.js release check with Web Crypto",
      "Chromium desktop 1440x900 evidence presentation"
    ]
  },
  "standards": [
    {
      "name": "Assertions and Protocols for SAML V2.0",
      "section": "Sections 2.4, 2.5, and 3.2",
      "url": "https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf"
    },
    {
      "name": "Bindings for SAML V2.0",
      "section": "HTTP Redirect and HTTP POST bindings",
      "url": "https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf"
    }
  ],
  "cases": [
    {
      "case_id": "SAML-RESPONSE-MATCH-001",
      "classification": "valid",
      "symptom": "A synthetic HTTP-POST response matches the expected issuer, audience, ACS, and request ID.",
      "safe_input": {
        "input": "PHNhbWxwOlJlc3BvbnNlCiAgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIKICB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIgogIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIgogIElEPSJfcmVzcG9uc2UtZXhhbXBsZS0xIgogIFZlcnNpb249IjIuMCIKICBJc3N1ZUluc3RhbnQ9IjIwMzAtMDEtMDFUMTI6MDA6MDBaIgogIERlc3RpbmF0aW9uPSJodHRwczovL3NwLmV4YW1wbGUudGVzdC9zYW1sL2FjcyIKICBJblJlc3BvbnNlVG89Il9yZXF1ZXN0LWV4YW1wbGUtMSI+CiAgPHNhbWw6SXNzdWVyPmh0dHBzOi8vaWRwLmV4YW1wbGUudGVzdC9tZXRhZGF0YTwvc2FtbDpJc3N1ZXI+CiAgPGRzOlNpZ25hdHVyZT4KICAgIDxkczpTaWduZWRJbmZvPgogICAgICA8ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPgogICAgICA8ZHM6UmVmZXJlbmNlIFVSST0iI19yZXNwb25zZS1leGFtcGxlLTEiPgogICAgICAgIDxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz4KICAgICAgICA8ZHM6RGlnZXN0VmFsdWU+YzJGbVpTMWxlR0Z0Y0d4bDwvZHM6RGlnZXN0VmFsdWU+CiAgICAgIDwvZHM6UmVmZXJlbmNlPgogICAgPC9kczpTaWduZWRJbmZvPgogICAgPGRzOlNpZ25hdHVyZVZhbHVlPmMyRm1aUzFsZUdGdGNHeGw8L2RzOlNpZ25hdHVyZVZhbHVlPgogIDwvZHM6U2lnbmF0dXJlPgogIDxzYW1scDpTdGF0dXM+CiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+CiAgPC9zYW1scDpTdGF0dXM+CiAgPHNhbWw6QXNzZXJ0aW9uIElEPSJfYXNzZXJ0aW9uLWV4YW1wbGUtMSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMzAtMDEtMDFUMTI6MDA6MDBaIj4KICAgIDxzYW1sOklzc3Vlcj5odHRwczovL2lkcC5leGFtcGxlLnRlc3QvbWV0YWRhdGE8L3NhbWw6SXNzdWVyPgogICAgPHNhbWw6U3ViamVjdD4KICAgICAgPHNhbWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj51c2VyQGV4YW1wbGUudGVzdDwvc2FtbDpOYW1lSUQ+CiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4KICAgICAgICA8c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YQogICAgICAgICAgSW5SZXNwb25zZVRvPSJfcmVxdWVzdC1leGFtcGxlLTEiCiAgICAgICAgICBSZWNpcGllbnQ9Imh0dHBzOi8vc3AuZXhhbXBsZS50ZXN0L3NhbWwvYWNzIgogICAgICAgICAgTm90T25PckFmdGVyPSIyMDk5LTAxLTAxVDAwOjAwOjAwWiIvPgogICAgICA8L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4KICAgIDwvc2FtbDpTdWJqZWN0PgogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjAtMDEtMDFUMDA6MDA6MDBaIiBOb3RPbk9yQWZ0ZXI9IjIwOTktMDEtMDFUMDA6MDA6MDBaIj4KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwczovL3NwLmV4YW1wbGUudGVzdC9tZXRhZGF0YTwvc2FtbDpBdWRpZW5jZT4KICAgICAgICA8c2FtbDpBdWRpZW5jZT5odHRwczovL2FsdGVybmF0ZS1zcC5leGFtcGxlLnRlc3QvbWV0YWRhdGE8L3NhbWw6QXVkaWVuY2U+CiAgICAgIDwvc2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgICAgIDxzYW1sOkF1ZGllbmNlPmh0dHBzOi8vc3AuZXhhbXBsZS50ZXN0L21ldGFkYXRhPC9zYW1sOkF1ZGllbmNlPgogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgIDwvc2FtbDpDb25kaXRpb25zPgogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQKICAgICAgQXV0aG5JbnN0YW50PSIyMDMwLTAxLTAxVDEyOjAwOjAwWiIKICAgICAgU2Vzc2lvbkluZGV4PSJfc2Vzc2lvbi1leGFtcGxlLTEiCiAgICAgIFNlc3Npb25Ob3RPbk9yQWZ0ZXI9IjIwOTktMDEtMDFUMDA6MDA6MDBaIj4KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0PgogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPgogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0PgogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50PgogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PgogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZW1haWwiIEZyaWVuZGx5TmFtZT0iRW1haWwiPgogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlPnVzZXJAZXhhbXBsZS50ZXN0PC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3NhbWw6QXR0cmlidXRlPgogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0icm9sZXMiIEZyaWVuZGx5TmFtZT0iUm9sZXMiPgogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlPmFkbWluPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICAgIDxzYW1sOkF0dHJpYnV0ZVZhbHVlPnJldmlld2VyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3NhbWw6QXR0cmlidXRlPgogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4KICA8L3NhbWw6QXNzZXJ0aW9uPgo8L3NhbWxwOlJlc3BvbnNlPg==",
        "mode": "post-base64",
        "now": "2030-01-01T12:05:00.000Z",
        "expectedIssuer": "https://idp.example.test/metadata",
        "expectedAudience": "https://sp.example.test/metadata",
        "expectedAcs": "https://sp.example.test/saml/acs",
        "expectedRequestId": "_request-example-1"
      },
      "expected_output": {
        "status": "decoded",
        "message_kind": "Response",
        "binding": "HTTP-POST Base64",
        "protocol_status": "urn:oasis:names:tc:SAML:2.0:status:Success",
        "mismatch_count": 0,
        "finding_codes": [
          "SIGNATURE_MARKER_PRESENT",
          "SIGNATURE_PLACEMENT_OBSERVATION"
        ]
      },
      "failure_code": null,
      "expected_outcome": "Continue to independent signature, trust, replay, and application-policy verification.",
      "fixture_sha256": "917638250d0a2ee2f4d2e80a6d72e6058cf98419f7173a31ef22af10ede67b8c"
    },
    {
      "case_id": "SAML-DESTINATION-RECIPIENT-002",
      "classification": "mismatch",
      "symptom": "Response Destination differs from SubjectConfirmationData Recipient and correlation also conflicts.",
      "safe_input": {
        "input": "<samlp:Response\n  xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"\n  xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\"\n  xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"\n  ID=\"_response-example-1\"\n  Version=\"2.0\"\n  IssueInstant=\"2030-01-01T12:00:00Z\"\n  Destination=\"https://sp.example.test/saml/acs\"\n  InResponseTo=\"_request-example-1\">\n  <saml:Issuer>https://idp.example.test/metadata</saml:Issuer>\n  <ds:Signature>\n    <ds:SignedInfo>\n      <ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\n      <ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"/>\n      <ds:Reference URI=\"#_response-example-1\">\n        <ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"/>\n        <ds:DigestValue>c2FmZS1leGFtcGxl</ds:DigestValue>\n      </ds:Reference>\n    </ds:SignedInfo>\n    <ds:SignatureValue>c2FmZS1leGFtcGxl</ds:SignatureValue>\n  </ds:Signature>\n  <samlp:Status>\n    <samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/>\n  </samlp:Status>\n  <saml:Assertion ID=\"_assertion-example-1\" Version=\"2.0\" IssueInstant=\"2030-01-01T12:00:00Z\">\n    <saml:Issuer>https://idp.example.test/metadata</saml:Issuer>\n    <saml:Subject>\n      <saml:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">user@example.test</saml:NameID>\n      <saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\">\n        <saml:SubjectConfirmationData\n          InResponseTo=\"_different-request\"\n          Recipient=\"https://sp.example.test/saml/other\"\n          NotOnOrAfter=\"2099-01-01T00:00:00Z\"/>\n      </saml:SubjectConfirmation>\n    </saml:Subject>\n    <saml:Conditions NotBefore=\"2020-01-01T00:00:00Z\" NotOnOrAfter=\"2099-01-01T00:00:00Z\">\n      <saml:AudienceRestriction>\n        <saml:Audience>https://sp.example.test/metadata</saml:Audience>\n        <saml:Audience>https://alternate-sp.example.test/metadata</saml:Audience>\n      </saml:AudienceRestriction>\n      <saml:AudienceRestriction>\n        <saml:Audience>https://sp.example.test/metadata</saml:Audience>\n      </saml:AudienceRestriction>\n    </saml:Conditions>\n    <saml:AuthnStatement\n      AuthnInstant=\"2030-01-01T12:00:00Z\"\n      SessionIndex=\"_session-example-1\"\n      SessionNotOnOrAfter=\"2099-01-01T00:00:00Z\">\n      <saml:AuthnContext>\n        <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>\n      </saml:AuthnContext>\n    </saml:AuthnStatement>\n    <saml:AttributeStatement>\n      <saml:Attribute Name=\"email\" FriendlyName=\"Email\">\n        <saml:AttributeValue>user@example.test</saml:AttributeValue>\n      </saml:Attribute>\n      <saml:Attribute Name=\"roles\" FriendlyName=\"Roles\">\n        <saml:AttributeValue>admin</saml:AttributeValue>\n        <saml:AttributeValue>reviewer</saml:AttributeValue>\n      </saml:Attribute>\n    </saml:AttributeStatement>\n  </saml:Assertion>\n</samlp:Response>",
        "mode": "raw-xml",
        "now": "2030-01-01T12:05:00.000Z",
        "expectedAcs": "https://sp.example.test/saml/acs"
      },
      "expected_output": {
        "status": "decoded-with-findings",
        "message_kind": "Response",
        "binding": "Raw XML",
        "protocol_status": "urn:oasis:names:tc:SAML:2.0:status:Success",
        "mismatch_count": 1,
        "finding_codes": [
          "EXPECTED_VALUE_MISMATCH",
          "IN_RESPONSE_TO_CONFLICT",
          "DESTINATION_RECIPIENT_CONFLICT",
          "SIGNATURE_MARKER_PRESENT",
          "SIGNATURE_PLACEMENT_OBSERVATION"
        ]
      },
      "failure_code": "DESTINATION_RECIPIENT_CONFLICT",
      "expected_outcome": "Reject until routing and correlation values match the intended service-provider flow.",
      "fixture_sha256": "505d2d08e4b5fac8d0725383ea250b7a931eb36d6201aabdd43fd1e254d885bb"
    },
    {
      "case_id": "SAML-POST-BASE64-MALFORMED-003",
      "classification": "malformed",
      "symptom": "An HTTP-POST value contains characters outside standard Base64.",
      "safe_input": {
        "input": "%%%=",
        "mode": "post-base64",
        "now": "2030-01-01T12:05:00.000Z"
      },
      "expected_output": {
        "status": "rejected",
        "message": "The HTTP-POST SAML value contains characters outside standard Base64."
      },
      "failure_code": "INVALID_BASE64",
      "expected_outcome": "Reject before XML parsing.",
      "fixture_sha256": "fcc91d837fc425bd620197223786bb61476abb4c7b57d989a5d400df12900f7b"
    }
  ]
}
