Skip to content

feat(pq-jws/ts): phase 2 - implement compact jws core utilities (ENG-1641)#19

Open
eacet wants to merge 2 commits intofeature/eng-1640from
feature/eng-1641
Open

feat(pq-jws/ts): phase 2 - implement compact jws core utilities (ENG-1641)#19
eacet wants to merge 2 commits intofeature/eng-1640from
feature/eng-1641

Conversation

@eacet
Copy link
Member

@eacet eacet commented Feb 26, 2026

Summary

Package(s)

Languages

  • TypeScript
  • Rust

Checklist

  • Tests pass for all modified packages
  • Linting/formatting passes (biome check, cargo fmt)
  • Both language implementations are consistent (or noted as follow-up)
  • Package README updated if public API changed
  • No unnecessary dependencies added

Related Issues

Copy link
Member Author

eacet commented Feb 26, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@greptile-apps
Copy link

greptile-apps bot commented Feb 26, 2026

Greptile Summary

This PR implements phase 2 of the compact JWS functionality, adding comprehensive base64url encoding/decoding and JWS compact format parsing with strict validation.

Key Changes:

  • Implemented base64url encoding/decoding with fallbacks for both browser (btoa/atob) and Node.js (Buffer) environments
  • Added robust JWS compact parsing with extensive validation: segment count, length limits, base64url format, UTF-8 validity, JSON structure, duplicate key detection (including escaped key normalization), and critical parameter handling
  • Implemented verifyJwsCompact function that parses tokens and delegates signature verification to provided callback
  • Added configurable parse options with sensible defaults and consistency checking for base64url expansion
  • Comprehensive test coverage for edge cases including trailing bit validation, malformed UTF-8, oversized inputs, and duplicate headers

Minor Observations:

  • Type definitions use interface instead of type for simple structures, which differs from project style preferences but doesn't affect functionality

Confidence Score: 5/5

  • This PR is safe to merge with very high confidence
  • The implementation is thorough and secure with comprehensive input validation, strict bounds checking, duplicate key detection, and extensive test coverage. The code follows defensive programming practices including UTF-8 validation, trailing bit verification, and protection against oversized inputs. Only minor style preferences noted that don't affect functionality.
  • No files require special attention - all implementations are solid with good test coverage

Important Files Changed

Filename Overview
packages/pq-jws/ts/src/base64url.ts Implements base64url encoding/decoding with comprehensive validation including padding rejection, character validation, and trailing bit verification
packages/pq-jws/ts/src/compact.ts Implements JWS compact parsing with extensive validation: duplicate key detection, critical parameter handling, bounds checking, and UTF-8/JSON validation
packages/pq-jws/ts/src/jws.ts Implements verifyJwsCompact function that delegates to verifier callback and validates return type is boolean
packages/pq-jws/ts/src/types.ts Adds parse options types and verification options, though uses interface instead of type for simple structures per project conventions

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start([verifyJwsCompact called]) --> Parse[parseJwsCompact]
    Parse --> ValidateFormat{Validate format:<br/>3 segments,<br/>non-empty header/signature}
    ValidateFormat -->|Invalid| FormatError[Throw JwsFormatError]
    ValidateFormat -->|Valid| DecodeHeader[Decode base64url header]
    DecodeHeader --> ValidateHeader{Validate header:<br/>UTF-8, JSON,<br/>required fields,<br/>no duplicates}
    ValidateHeader -->|Invalid| ValidationError[Throw JwsValidationError]
    ValidateHeader -->|Valid| DecodePayload[Decode base64url payload]
    DecodePayload --> DecodeSignature[Decode base64url signature]
    DecodeSignature --> CheckBounds{Check size bounds:<br/>header, payload,<br/>signature lengths}
    CheckBounds -->|Exceeded| ValidationError
    CheckBounds -->|Within limits| BuildSigningInput[Build signing input:<br/>header.payload]
    BuildSigningInput --> CallVerifier[Call verifier callback<br/>with signing input + signature]
    CallVerifier --> CheckResult{Verifier returns<br/>boolean?}
    CheckResult -->|No| ValidationError
    CheckResult -->|Yes| ReturnResult([Return verification result])
    
    style Start fill:#e1f5ff
    style ReturnResult fill:#e1f5ff
    style FormatError fill:#ffebee
    style ValidationError fill:#ffebee
    style CallVerifier fill:#f3e5f5
Loading

Last reviewed commit: f56a36e

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9aac90db59

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

};

function maxEncodedLength(decodedLength: number): number {
return Math.ceil(decodedLength / 3) * 4;

Choose a reason for hiding this comment

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

P2 Badge Use unpadded expansion when validating compact bounds

resolveJwsCompactParseOptions currently treats each segment’s worst-case encoded size as padded Base64 (Math.ceil(n/3)*4), but compact JWS uses unpadded base64url, so this overestimates by up to 2 chars per segment and rejects valid tight configurations. For example, options that are valid under unpadded encoding (and would parse correctly) can fail early with “internally inconsistent” purely because of this padded-length math, which makes size hardening with custom limits unnecessarily unusable.

Useful? React with 👍 / 👎.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +54 to +65
export interface JwsCompactParseOptions {
maxCompactLength: number;
maxHeaderLength: number;
maxPayloadLength: number;
maxSignatureLength: number;
}

export type JwsCompactParseOptionsInput = Partial<JwsCompactParseOptions>;

export interface VerifyJwsCompactOptions {
parseOptions?: JwsCompactParseOptionsInput;
}
Copy link

Choose a reason for hiding this comment

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

Consider using type instead of interface for these simple data structures that don't use inheritance or extension, per project conventions. For example:

Suggested change
export interface JwsCompactParseOptions {
maxCompactLength: number;
maxHeaderLength: number;
maxPayloadLength: number;
maxSignatureLength: number;
}
export type JwsCompactParseOptionsInput = Partial<JwsCompactParseOptions>;
export interface VerifyJwsCompactOptions {
parseOptions?: JwsCompactParseOptionsInput;
}
export type JwsCompactParseOptions = {
maxCompactLength: number;
maxHeaderLength: number;
maxPayloadLength: number;
maxSignatureLength: number;
};
export type JwsCompactParseOptionsInput = Partial<JwsCompactParseOptions>;
export type VerifyJwsCompactOptions = {
parseOptions?: JwsCompactParseOptionsInput;
};

Context Used: Rule from dashboard - Use type by default in TypeScript unless you specifically need interface features like extension... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/pq-jws/ts/src/types.ts
Line: 54-65

Comment:
Consider using `type` instead of `interface` for these simple data structures that don't use inheritance or extension, per project conventions. For example:

```suggestion
export type JwsCompactParseOptions = {
  maxCompactLength: number;
  maxHeaderLength: number;
  maxPayloadLength: number;
  maxSignatureLength: number;
};

export type JwsCompactParseOptionsInput = Partial<JwsCompactParseOptions>;

export type VerifyJwsCompactOptions = {
  parseOptions?: JwsCompactParseOptionsInput;
};
```

**Context Used:** Rule from `dashboard` - Use `type` by default in TypeScript unless you specifically need `interface` features like extension... ([source](https://app.greptile.com/review/custom-context?memory=c862f053-5655-4b41-be69-c840e3c9f280))

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link

greptile-apps bot commented Feb 26, 2026

Additional Comments (1)

packages/pq-jws/ts/src/types.ts
Most of these interfaces could be converted to type declarations per project style preferences, except JwsVerifierContext which uses extends (though it could also be simplified to type JwsVerifierContext = JwsSignerContext).

Context Used: Rule from dashboard - Use type instead of interface for DTOs and simple data structures that don't use inheritance or ... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/pq-jws/ts/src/types.ts
Line: 1-52

Comment:
Most of these interfaces could be converted to `type` declarations per project style preferences, except `JwsVerifierContext` which uses `extends` (though it could also be simplified to `type JwsVerifierContext = JwsSignerContext`).

**Context Used:** Rule from `dashboard` - Use `type` instead of `interface` for DTOs and simple data structures that don't use inheritance or ... ([source](https://app.greptile.com/review/custom-context?memory=2b2a7a55-162e-44b9-8c4c-3f52514f7037))

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

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.

1 participant