8000 fix: handle malformed JWT encoding by marco-a-affinidi · Pull Request #304 · affinidi/affinidi-ssi-dart · GitHub
[go: up one dir, main page]

Skip to content

fix: handle malformed JWT encoding - #304

Open
marco-a-affinidi wants to merge 1 commit into
mainfrom
fix/malformed_jwt_exceptions
Open

marco-a-affinidi wants to merge 1 commit into
mainfrom
fix/malformed_jwt_exceptions

Conversation

@marco-a-affinidi
Copy link
Copy Markdown
Contributor

This pull request introduces improved error handling for JWT parsing and verification, ensuring that malformed JWTs and invalid encodings are consistently rejected with typed exceptions. It also adds comprehensive tests to validate these behaviors. The main changes are grouped into error handling improvements in the core logic and expanded test coverage.

Error handling improvements:

  • Refactored JWT parsing in JwtParser to use a new _decodeJsonSegment helper, which throws a typed SsiException if a JWT header or payload is not valid base64url or not a JSON object. [1] [2]
  • Updated JwtDm1Suite.verifyIntegrity to catch and rethrow signature decoding errors as SsiException with an explicit error code for invalid encoding.
  • Simplified header decoding in JwtDm1Suite.verifyIntegrity by using the parsed JWS header directly, improving consistency.
  • Added missing import for dart:typed_data to support stricter type handling.

Test coverage:

  • Added new tests in jwt_parser_test.dart to verify that malformed JWTs (invalid base64url, non-object JSON headers or payloads) are rejected with the correct typed exception.
  • Added a test in jwt_header_jwk_validation_test.dart to ensure verifyIntegrity rejects malformed serialized JWT input with a typed exception.

@github-actions
github-actions Bot commented Sep 8, 2026
Copy link
Copy Markdown

Coverage after merging fix/malformed_jwt_exceptions into main will be

79.50%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
lib/src
   types.dart70.83%100%100%70.83%133, 172–173, 185, 187, 202, 257
   digest_utils.dart100%100%100%100%
   utility.dart100%100%100%100%
lib/src/credentials/jwt
   jwt_data_model_v1.dart100%100%100%100%
   jwt_dm_v1_suite.dart81.48%100%100%81.48%101, 103, 151, 153–154, 40, 42, 60, 75, 77
lib/src/credentials/linked_data
   ld_base_suite.dart90.77%100%100%90.77%121, 123, 169, 172, 206, 212
   ld_dm_v1_suite.dart100%100%100%100%
   ld_dm_v2_suite.dart100%100%100%100%
lib/src/credentials/models
   sdjwt_signer_adapter.dart100%100%100%100%
lib/src/credentials/models/field_types
   issuer.dart100%100%100%100%
   context.dart85.96%100%100%85.96%171, 173, 206–211
   credential_schema.dart100%100%100%100%
   credential_subject.dart72.97%100%100%72.97%38, 55, 57–58, 61, 64–65, 70–71, 94
   evidence.dart76.47%100%100%76.47%50–52, 54
   holder.dart100%100%100%100%
   terms_of_use.dart75%100%100%75%46–48, 50
lib/src/credentials/models/field_types/credential_status
   revocation_list_2020.dart20.90%100%100%20.90%101–104, 107, 110–112, 114–116, 118–120, 122–124, 14, 171, 173, 175–182, 22–27, 65, 68–75, 81, 84–85, 87–88, 90–91, 93–94, 99
   v1.dart72.73%100%100%72.73%60–61, 71, 73–76, 78, 82–83, 88–89
   v2.dart72.73%100%100%72.73%59–60, 70, 72–75, 77, 81–82, 87–88
lib/src/credentials/models/field_types/refresh_service
   v1.dart76.47%100%100%76.47%46–48, 50
   v2.dart18.18%100%100%18.18%35–36, 38, 55–56, 61, 66–67, 69
lib/src/credentials/models/v1
   mutable_vc_data_model_v1.dart88.14%100%100%88.14%154, 202–203, 209–210, 216, 222
   vc_data_model_v1.dart91.27%100%100%91.27%160, 162–163, 187–189, 212–215, 218
lib/src/credentials/models/v2
   mutable_vc_data_model_v2.dart84.75%100%100%84.75%179, 185–186, 196–197, 204–205, 211, 217
   vc_data_model_v2.dart90.76%100%100%90.76%165–167, 190–193, 196, 316, 322, 326
lib/src/credentials/parsers
   jwt_parser.dart91.67%100%100%91.67%51, 53
   ld_parser.dart85.71%100%100%85.71%26
   sdjwt_parser.dart70%100%100%70%40, 56, 58
lib/src/credentials/presentations/linked_data
   ld_vp_dm_v1_suite.dart100%100%100%100%
   ld_vp_dm_v2_suite.dart100%100%100%100%
lib/src/credentials/presentations/models
   vc_parse_present.dart57.14%100%100%57.14%101, 104, 109–110, 35–37, 62–64, 88–89, 92–97
lib/src/credentials/presentations/models/v1
   mu 8000 table_vp_data_model_v1.dart48.15%100%100%48.15%102, 75, 81–82, 84–86, 88, 90, 92, 94–95, 98–99
   vp_data_model_v1.dart88.52%100%100%88.52%111, 113, 138, 143, 84, 86–87
lib/src/credentials/presentations/models/v2
   vp_data_model_v2.dart88.06%100%100%88.06%116, 118, 142, 147, 205, 89, 91–92
   mutable_vp_data_model_v2.dart46.88%100%100%46.88%101–102, 105–106, 109, 111–112, 115, 81, 87–88, 90, 92–93, 95, 97, 99
lib/src/credentials/presentations/suites
   universal_presentation_parser.dart42.86%100%100%42.86%21–22, 24–25
   universal_presentation_verifier.dart90.63%100%100%90.63%106–108
   vp_suites.dart61.54%100%100%61.54%31, 33, 53–55
lib/src/credentials/presentations/verification
   delegation_vc_verifier.dart100%100%100%100%
   holder_binding_verifier.dart94.87%100%100%94.87%64–65
   vp_expiry_verifier.dart100%100%100%100%
   vp_integrity_verifier.dart100%100%100%100%
   vp_domain_challenge_verifier.dart75%100%100%75%36, 53–54, 62–63
lib/src/credentials/proof
   data_integrity_ecdsa_suite.dart77.98%100%100%77.98%143, 152, 155, 158, 161, 164, 171, 174, 181, 268, 272, 274–275, 350, 355, 384, 386, 404, 408, 410–411, 52, 54–55
   data_integrity_eddsa_suite.dart80%100%100%80%137, 146, 149, 152, 155, 158, 165, 168, 175, 181, 49, 51–52
   data_integrity_mldsa_suite.dart91.14%100%100%91.14%277, 279–280, 60, 68–70
   ecdsa_secp256k1_signature2019_suite.dart75.61%100%100%75.61%125, 130–131, 133, 135–136, 138, 140–141, 143, 145–146, 148, 152–153, 155, 158, 76, 78, 81
   embedded_proof_suite.dart100%100%100%100%
   jcs_utils.dart49.06%100%100%49.06%17, 193, 196, 199–202, 221, 225, 229, 240–241, 249, 252, 42, 44–45, 53, 55, 61, 64, 72–73, 75, 79, 81, 84
   embedded_proof.dart75%100%100%75%107–108, 110, 117, 158–159, 165–167, 169, 183, 191, 197, 199, 91, 96–97
   base_data_integrity_verifier.dart63.50%100%100%63.50%101–102, 116–119, 123–127, 154–159, 183–184, 211, 213, 215–216, 219, 222–223, 227–228, 302, 304–305, 339, 344–345, 347, 349–350, 352, 354–355, 357, 359–360, 362, 365–366, 368, 371, 87–88
   proof_validation_utils.dart100%100%100%100%
   base_jcs_generator.dart100%100%100%100%
   data_integrity_context_util.dart60%100%100%60%14, 23, 25, 31, 35,

@affinidi-appsecurity-bot
Copy link
Copy Markdown

🛡️ AI Agentic Security Code Review

1 AI-confirmed issue.

Mandatory to check: 🔒 Security Code Review Report — Security tab ↗

📊 Summary & findings
✅ Confirmed ⚠️ Must-Review-By-Human
1 0

Finding details (titles, evidence + remediation) are in the 🔒 Security tab ↗ — visible to maintainers / code owners only.

return false;
}

final jws = decode(input.serialized);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also handle malformed input.serialized here?

base64UrlNoPadDecode(segments[1]),
),
) as Map<String, dynamic>;
final header = _decodeJsonSegment(segments[0], 'header');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here for header and payload, should we handle malformed data? So the main question is, if we handle a malformed signature, should we handle the other JWT parts? Can base64UrlNoPadDecode, _decodeJsonSegment, and other decoding methods so they throw SSI exception. In this case, we don't have to wrap try catch here and just allow them to throw exceptions from decoders.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

0