Skip to content

Replace JWT dependency and remove vulnerable crypto chain#992

Open
PeterDaveHello wants to merge 1 commit into
ChatGPTBox-dev:masterfrom
PeterDaveHello:secure-jwt-dependency-cleanup
Open

Replace JWT dependency and remove vulnerable crypto chain#992
PeterDaveHello wants to merge 1 commit into
ChatGPTBox-dev:masterfrom
PeterDaveHello:secure-jwt-dependency-cleanup

Conversation

@PeterDaveHello

@PeterDaveHello PeterDaveHello commented Jul 1, 2026

Copy link
Copy Markdown
Member

Use a focused HS256 signer backed by @noble/hashes for runtime tokens and Firefox AMO metadata signing. This keeps signing limited to the algorithm this project uses instead of carrying a Node crypto polyfill tree into browser builds.

Preserve the actual runtime JWT claim set, including the implicit iat claim previously added by jsonwebtoken, the custom runtime header, and AMO authorization format. Cache entries now track a SHA-256 API-key fingerprint so key changes regenerate tokens without retaining the raw secret.

Update uuid for dependency hygiene. Keep pre-commit on its existing 1.x hook behavior and pin its transitive cross-spawn dependency to 7.0.6 with npm overrides. Add tests that verify HS256 signatures with node:crypto and cover runtime cache reuse, key changes, expiration, invalid keys, retry token uniqueness, and AMO signing.

Summary by CodeRabbit

  • New Features
    • Added dedicated HS256 JWT signing and cryptographic validation used for token generation and release submission.
    • JWT caching now tracks the API key used and regenerates on API key changes and expiration.
  • Bug Fixes
    • Release submission retries now generate a new Authorization JWT per attempt.
    • Updated non-minimal browser build shims/aliases for more consistent behavior.
  • Chores
    • Updated dependency set and enforced a specific cross-spawn version for pre-commit.
  • Tests
    • Expanded JWT test coverage, including signature verification, UTF-8 handling, and retry timing/claims checks.

@PeterDaveHello PeterDaveHello requested a review from Copilot July 1, 2026 18:30
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 066f3955-2ea2-4e78-bd30-7a210141badd

📥 Commits

Reviewing files that changed from the base of the PR and between 59d9ce3 and 3816d6e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • build.mjs
  • package.json
  • scripts/submit-stores.mjs
  • src/utils/hs256-jwt.mjs
  • src/utils/jwt-token-generator.mjs
  • tests/unit/release/submit-stores.test.mjs
  • tests/unit/utils/hs256-jwt.test.mjs
  • tests/unit/utils/jwt-token-generator.test.mjs
💤 Files with no reviewable changes (1)
  • build.mjs
🚧 Files skipped from review as they are similar to previous changes (6)
  • scripts/submit-stores.mjs
  • tests/unit/utils/hs256-jwt.test.mjs
  • src/utils/jwt-token-generator.mjs
  • src/utils/hs256-jwt.mjs
  • tests/unit/utils/jwt-token-generator.test.mjs
  • tests/unit/release/submit-stores.test.mjs

📝 Walkthrough

Walkthrough

Replaces jsonwebtoken with a custom HS256 signer, updates token generation and Firefox submission flows to use it, removes crypto-browserify, adjusts webpack aliases, and refreshes related tests and dependency versions.

Changes

JWT signing migration

Layer / File(s) Summary
HS256 JWT signer utility
src/utils/hs256-jwt.mjs, tests/unit/utils/hs256-jwt.test.mjs
Adds signHs256Jwt plus Base64URL helpers, runtime validation, and HMAC-SHA256 signing; tests cover headers, validation, ordering, and UTF-8 inputs.
Token generator uses signHs256Jwt with API-key-aware caching
src/utils/jwt-token-generator.mjs, tests/unit/utils/jwt-token-generator.test.mjs
Switches JWT creation to the custom signer, adds API-key-aware cache invalidation, and updates tests to decode and verify tokens locally.
Firefox JWT creation in submit-stores script
scripts/submit-stores.mjs, tests/unit/release/submit-stores.test.mjs
Uses the custom signer for Firefox JWTs, rebuilds retry Authorization headers per attempt, and updates tests to verify signed headers and retry timing.
Dependency and build config updates
package.json, build.mjs
Updates dependencies to add @noble/hashes, remove jsonwebtoken and crypto-browserify, bump uuid and pre-commit, and change non-minimal webpack aliases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested labels: Review effort 4/5

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: replacing the JWT dependency and removing the vulnerable crypto chain.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request replaces the heavy jsonwebtoken and crypto-browserify dependencies with a lightweight, custom HS256 JWT signing utility (hs256-jwt.mjs) powered by @noble/hashes. It also updates the token generator to regenerate tokens when the API key changes, upgrades several dependencies (such as uuid and pre-commit), and adapts the unit tests to verify tokens without relying on jsonwebtoken. Feedback is provided regarding a security vulnerability in the custom signing utility where an undefined secret would be encoded as the literal string "undefined", resulting in a weak signature; adding a validation check to ensure secret is a non-empty string is recommended.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/utils/hs256-jwt.mjs Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Replace jsonwebtoken with minimal HS256 signer and drop crypto-browserify

🐞 Bug fix ✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Replace jsonwebtoken with a minimal HS256 JWT signer using @noble/hashes.
• Remove crypto-browserify polyfill wiring to shrink browser bundles and reduce supply-chain risk.
• Strengthen token caching to regenerate on API key changes, and add signature-focused tests.
Diagram

graph TD
  A["scripts/submit-stores.mjs"] --> B["src/utils/hs256-jwt.mjs"] --> C{{"@noble/hashes"}}
  D["src/utils/jwt-token-generator.mjs"] --> B
  E["unit tests"] --> A & D & B
  F(["build.mjs + package.json"]) -. "deps / polyfills" .-> B

  subgraph Legend
    direction LR
    _mod["Module"] ~~~ _cfg(["Config"]) ~~~ _ext{{"External dep"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use a general-purpose JWT library (e.g., jose) with tree-shaking
  • ➕ Mature JWT implementation with broader algorithm and validation support
  • ➕ Often provides robust base64url handling and environment abstractions
  • ➖ Heavier dependency footprint than HS256-only needs
  • ➖ May still pull in crypto/polyfill complexity depending on build targets
2. Use WebCrypto/SubtleCrypto for HMAC in browser and node:crypto in Node
  • ➕ Avoids crypto dependencies entirely when WebCrypto is available
  • ➕ Leverages platform primitives directly
  • ➖ Async APIs complicate call sites that currently expect sync signing
  • ➖ Requires conditional implementations and more environment-specific logic
3. Keep jsonwebtoken but hard-externalize crypto polyfills from browser bundles
  • ➕ Minimal code change to signing logic
  • ➕ Avoids maintaining custom JWT assembly code
  • ➖ Retains the broader dependency/supply-chain surface of jsonwebtoken
  • ➖ Bundler config can become fragile across environments and updates

Recommendation: The PR’s approach (a small HS256-only signer backed by @noble/hashes) is a good fit given the project’s fixed algorithm requirements and the goal of removing the crypto-browserify chain from browser builds. One follow-up worth considering is hardening the base64url encoding for Node execution contexts (the release script runs under Node): if Node versions without global btoa/TextEncoder are supported, add a Buffer-based fallback to avoid runtime surprises while keeping the browser-friendly path.

Files changed (8) +169 / -22

Enhancement (2) +30 / -3
submit-stores.mjsSwitch AMO JWT signing to HS256 utility +2/-3

Switch AMO JWT signing to HS256 utility

• Replaces jsonwebtoken-based signing with the new HS256 signer while preserving the AMO JWT claims (iss/jti/iat/exp) and the existing Authorization format expected by Firefox AMO APIs.

scripts/submit-stores.mjs

hs256-jwt.mjsIntroduce minimal HS256 JWT signer +28/-0

Introduce minimal HS256 JWT signer

• Adds a small utility that base64url-encodes header/payload and signs the resulting input using HMAC-SHA256 from @noble/hashes. Supports a default JWT header as well as caller-provided custom headers.

src/utils/hs256-jwt.mjs

Bug fix (1) +11 / -10
jwt-token-generator.mjsUse HS256 signer and regenerate cache on API key changes +11/-10

Use HS256 signer and regenerate cache on API key changes

• Migrates runtime token generation from jsonwebtoken to the HS256 utility and adds iat to the payload. Strengthens cache invalidation by tracking the API key so tokens are regenerated when the signing key changes, even within the expiration window.

src/utils/jwt-token-generator.mjs

Tests (3) +125 / -4
submit-stores.test.mjsVerify AMO JWT signatures using node:crypto +27/-0

Verify AMO JWT signatures using node:crypto

• Adds helpers to decode JWT parts and verify HS256 signatures via node:crypto HMAC. Extends the AMO submission tests to assert the token header fields and time window semantics (exp-iat == 300) plus UUID formatting for jti.

tests/unit/release/submit-stores.test.mjs

hs256-jwt.test.mjsAdd unit tests for HS256 signer utility +57/-0

Add unit tests for HS256 signer utility

• Introduces test coverage validating default headers, custom header preservation, and correct handling of UTF-8 payload/secret values. Verification is performed using node:crypto to ensure interoperability.

tests/unit/utils/hs256-jwt.test.mjs

jwt-token-generator.test.mjsUpdate token generator tests for signature and cache semantics +41/-4

Update token generator tests for signature and cache semantics

• Replaces jsonwebtoken decode/verify usage with local decoding and node:crypto-based HS256 verification. Adds assertions for iat, and adds a new test ensuring cached tokens are regenerated when the API key changes.

tests/unit/utils/jwt-token-generator.test.mjs

Other (2) +3 / -5
build.mjsDrop webpack crypto-browserify polyfill +0/-1

Drop webpack crypto-browserify polyfill

• Removes the explicit crypto polyfill mapping from the webpack fallback configuration, preventing crypto-browserify and its dependency chain from being bundled into browser builds.

build.mjs

package.jsonReplace JWT/crypto deps; bump uuid and pre-commit +3/-4

Replace JWT/crypto deps; bump uuid and pre-commit

• Adds @noble/hashes and removes jsonwebtoken and crypto-browserify from runtime dependencies. Updates uuid and pre-commit to newer pinned ranges to address known issues without broad upgrade churn.

package.json

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces the jsonwebtoken + browser crypto polyfill dependency chain with a small, project-scoped HS256 JWT signer (backed by @noble/hashes) used for both runtime tokens and Firefox AMO authorization, while keeping existing token formats and improving cache behavior to account for API key changes.

Changes:

  • Introduce signHs256Jwt (HS256-only signing) and migrate runtime + AMO JWT generation off jsonwebtoken.
  • Update runtime token caching to regenerate when the API key changes (not just on expiry) and preserve iat behavior.
  • Remove crypto-browserify build fallback and add/adjust unit tests to verify HS256 signatures and cache behavior.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/unit/utils/jwt-token-generator.test.mjs Replaces jsonwebtoken decode/verify with explicit HS256 verification + adds cache/key-change coverage.
tests/unit/utils/hs256-jwt.test.mjs Adds focused unit tests for the new HS256 JWT signer utility.
tests/unit/release/submit-stores.test.mjs Verifies AMO Authorization JWT structure/signature without jsonwebtoken.
src/utils/jwt-token-generator.mjs Switches runtime token generation to the new signer and extends cache invalidation to include API key changes.
src/utils/hs256-jwt.mjs New HS256 JWT signing helper built on @noble/hashes.
scripts/submit-stores.mjs Migrates AMO JWT creation from jsonwebtoken to the new HS256 signer.
package.json Drops jsonwebtoken/crypto-browserify, adds @noble/hashes, bumps uuid and pre-commit.
package-lock.json Lockfile updates reflecting dependency removals/additions and version bumps.
build.mjs Removes webpack crypto fallback to drop the browser crypto polyfill tree.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/hs256-jwt.mjs Outdated
Comment thread tests/unit/utils/hs256-jwt.test.mjs
@qodo-code-review

qodo-code-review Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Caches API key secret ✓ Resolved 🐞 Bug ⛨ Security
Description
jwt-token-generator.mjs now stores the full apiKey string (format id.secret) in module-scope
state (tokenApiKey) to decide cache reuse, which prolongs the in-memory lifetime of the secret
portion. This increases secret exposure in the event of in-process memory disclosure/debugging while
the cached token remains valid.
Code

src/utils/jwt-token-generator.mjs[R32-39]

+  tokenApiKey = apiKey
+  tokenExpiration = payload.exp * 1000
}

-function shouldRegenerateToken() {
+function shouldRegenerateToken(apiKey) {
  const ms = Date.now()
-  return !jwtToken || ms >= tokenExpiration
+  return !jwtToken || apiKey !== tokenApiKey || ms >= tokenExpiration
}
Evidence
The generator explicitly treats apiKey as id.secret and then persists the full string in
tokenApiKey, which is later compared against the incoming apiKey to decide whether to reuse the
cached JWT—therefore the secret portion remains in module-scope memory across calls.

src/utils/jwt-token-generator.mjs[7-16]
src/utils/jwt-token-generator.mjs[27-39]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/utils/jwt-token-generator.mjs` caches `tokenApiKey = apiKey`, where `apiKey` is parsed as `id.secret`. This retains the raw secret in module-scope memory for the full cache window.

## Issue Context
The cache only needs a stable identifier to detect when the API key changes; it does not need the raw secret string.

## Fix Focus Areas
- src/utils/jwt-token-generator.mjs[3-39]

## Suggested fix
- Replace `tokenApiKey` with a non-reversible fingerprint, e.g. `tokenApiKeyFingerprint`.
- Compute fingerprint synchronously when generating the token (and when checking regeneration), for example:
 - `sha256(TextEncoder().encode(apiKey))` and store as hex/base64url (using `@noble/hashes/utils` like `bytesToHex`), or
 - an HMAC-based fingerprint with a constant message.
- Compare fingerprints instead of comparing raw `apiKey` strings.
- Ensure behavior stays the same: token regenerates when either the key changes, the token is missing, or it is expired.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Unvalidated header param ✓ Resolved 🐞 Bug ☼ Reliability
Description
signHs256Jwt dereferences header.alg/header.typ without first validating that header is a
non-null object, so callers passing null or a non-object will get a generic runtime TypeError
instead of a clear validation error. This makes failures harder to diagnose and is inconsistent with
the explicit payload/secret validation in the same function.
Code

src/utils/hs256-jwt.mjs[R19-36]

+function signHs256Jwt(payload, secret, header = {}) {
+  if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
+    throw new TypeError('JWT payload must be a non-array object')
+  }
+  if (typeof secret !== 'string' || secret.length === 0) {
+    throw new TypeError('JWT secret must be a non-empty string')
+  }
+  if (header.alg !== undefined && header.alg !== 'HS256') {
+    throw new Error('signHs256Jwt only supports HS256')
+  }
+  if (header.typ !== undefined && header.typ !== 'JWT') {
+    throw new Error('signHs256Jwt only supports JWT typ')
+  }
+
+  const customHeader = { ...header }
+  delete customHeader.alg
+  delete customHeader.typ
+  const encodedHeader = encodeJson({ alg: 'HS256', typ: 'JWT', ...customHeader })
Evidence
The implementation validates payload and secret but directly reads header.alg / header.typ
and spreads header without validating header is an object, which will throw a generic runtime
error for invalid inputs like null.

src/utils/hs256-jwt.mjs[19-36]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`signHs256Jwt(payload, secret, header = {})` assumes `header` is an object and accesses `header.alg` / `header.typ` immediately. If a caller passes `null`, a string, etc., the function throws a generic JS `TypeError` (e.g., cannot read properties of null) rather than a clear validation error.

## Issue Context
The function already performs explicit validation for `payload` and `secret`, so adding a similar guard for `header` keeps behavior consistent and produces actionable error messages.

## Fix Focus Areas
- src/utils/hs256-jwt.mjs[19-36]

## Suggested change
Add a guard near the top of `signHs256Jwt`:
- If `header` is not a non-null, non-array object, throw a `TypeError` with a clear message (e.g., `JWT header must be a non-array object`).
- Keep default behavior the same when `header` is `undefined` (still default `{}`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 3816d6e

Results up to commit be5a63f


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Remediation recommended
1. Caches API key secret ✓ Resolved 🐞 Bug ⛨ Security
Description
jwt-token-generator.mjs now stores the full apiKey string (format id.secret) in module-scope
state (tokenApiKey) to decide cache reuse, which prolongs the in-memory lifetime of the secret
portion. This increases secret exposure in the event of in-process memory disclosure/debugging while
the cached token remains valid.
Code

src/utils/jwt-token-generator.mjs[R32-39]

+  tokenApiKey = apiKey
+  tokenExpiration = payload.exp * 1000
}

-function shouldRegenerateToken() {
+function shouldRegenerateToken(apiKey) {
  const ms = Date.now()
-  return !jwtToken || ms >= tokenExpiration
+  return !jwtToken || apiKey !== tokenApiKey || ms >= tokenExpiration
}
Evidence
The generator explicitly treats apiKey as id.secret and then persists the full string in
tokenApiKey, which is later compared against the incoming apiKey to decide whether to reuse the
cached JWT—therefore the secret portion remains in module-scope memory across calls.

src/utils/jwt-token-generator.mjs[7-16]
src/utils/jwt-token-generator.mjs[27-39]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/utils/jwt-token-generator.mjs` caches `tokenApiKey = apiKey`, where `apiKey` is parsed as `id.secret`. This retains the raw secret in module-scope memory for the full cache window.

## Issue Context
The cache only needs a stable identifier to detect when the API key changes; it does not need the raw secret string.

## Fix Focus Areas
- src/utils/jwt-token-generator.mjs[3-39]

## Suggested fix
- Replace `tokenApiKey` with a non-reversible fingerprint, e.g. `tokenApiKeyFingerprint`.
- Compute fingerprint synchronously when generating the token (and when checking regeneration), for example:
 - `sha256(TextEncoder().encode(apiKey))` and store as hex/base64url (using `@noble/hashes/utils` like `bytesToHex`), or
 - an HMAC-based fingerprint with a constant message.
- Compare fingerprints instead of comparing raw `apiKey` strings.
- Ensure behavior stays the same: token regenerates when either the key changes, the token is missing, or it is expired.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 2f2adca


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Informational
1. Unvalidated header param ✓ Resolved 🐞 Bug ☼ Reliability
Description
signHs256Jwt dereferences header.alg/header.typ without first validating that header is a
non-null object, so callers passing null or a non-object will get a generic runtime TypeError
instead of a clear validation error. This makes failures harder to diagnose and is inconsistent with
the explicit payload/secret validation in the same function.
Code

src/utils/hs256-jwt.mjs[R19-36]

+function signHs256Jwt(payload, secret, header = {}) {
+  if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
+    throw new TypeError('JWT payload must be a non-array object')
+  }
+  if (typeof secret !== 'string' || secret.length === 0) {
+    throw new TypeError('JWT secret must be a non-empty string')
+  }
+  if (header.alg !== undefined && header.alg !== 'HS256') {
+    throw new Error('signHs256Jwt only supports HS256')
+  }
+  if (header.typ !== undefined && header.typ !== 'JWT') {
+    throw new Error('signHs256Jwt only supports JWT typ')
+  }
+
+  const customHeader = { ...header }
+  delete customHeader.alg
+  delete customHeader.typ
+  const encodedHeader = encodeJson({ alg: 'HS256', typ: 'JWT', ...customHeader })
Evidence
The implementation validates payload and secret but directly reads header.alg / header.typ
and spreads header without validating header is an object, which will throw a generic runtime
error for invalid inputs like null.

src/utils/hs256-jwt.mjs[19-36]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`signHs256Jwt(payload, secret, header = {})` assumes `header` is an object and accesses `header.alg` / `header.typ` immediately. If a caller passes `null`, a string, etc., the function throws a generic JS `TypeError` (e.g., cannot read properties of null) rather than a clear validation error.

## Issue Context
The function already performs explicit validation for `payload` and `secret`, so adding a similar guard for `header` keeps behavior consistent and produces actionable error messages.

## Fix Focus Areas
- src/utils/hs256-jwt.mjs[19-36]

## Suggested change
Add a guard near the top of `signHs256Jwt`:
- If `header` is not a non-null, non-array object, throw a `TypeError` with a clear message (e.g., `JWT header must be a non-array object`).
- Keep default behavior the same when `header` is `undefined` (still default `{}`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/utils/hs256-jwt.mjs`:
- Around line 19-23: The signHs256Jwt helper should reject missing/invalid
secrets and stop accepting caller-provided JWT algorithms. Update signHs256Jwt
to validate secret before TextEncoder.encode(secret) so undefined/null/empty
values fail fast, and pin the header used by encodeJson to HS256/typ JWT instead
of trusting the header argument. Keep the HMAC-SHA256 signing logic in sync with
the header in signHs256Jwt so runtime tokens and AMO auth always produce
self-consistent JWTs.

In `@src/utils/jwt-token-generator.mjs`:
- Around line 18-29: The cache expiration in jwt-token-generator’s token
creation logic is computed from the millisecond clock, while the JWT payload exp
in the same function is based on whole seconds, so align the cache cutoff to the
same rounded seconds boundary used for exp. Update the token creation flow in
jwt-token-generator.mjs so tokenExpiration is derived from the same
currentSeconds/exp basis as jwtToken generation, ensuring getToken() stops
reusing a token as soon as its JWT is considered expired.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 08e8fd43-80d9-4db7-b50f-3f08c7d1303f

📥 Commits

Reviewing files that changed from the base of the PR and between ff3d5f7 and b3d8480.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • build.mjs
  • package.json
  • scripts/submit-stores.mjs
  • src/utils/hs256-jwt.mjs
  • src/utils/jwt-token-generator.mjs
  • tests/unit/release/submit-stores.test.mjs
  • tests/unit/utils/hs256-jwt.test.mjs
  • tests/unit/utils/jwt-token-generator.test.mjs
💤 Files with no reviewable changes (1)
  • build.mjs

Comment thread src/utils/hs256-jwt.mjs Outdated
Comment thread src/utils/jwt-token-generator.mjs Outdated
@PeterDaveHello PeterDaveHello force-pushed the secure-jwt-dependency-cleanup branch 2 times, most recently from 692555a to be5a63f Compare July 2, 2026 16:49
Comment thread src/utils/jwt-token-generator.mjs Outdated
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit be5a63f

@PeterDaveHello PeterDaveHello force-pushed the secure-jwt-dependency-cleanup branch from be5a63f to dbccdfe Compare July 2, 2026 17:25
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit dbccdfe

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Comment thread src/utils/jwt-token-generator.mjs
Comment on lines +51 to +53
return (
!jwtToken || getApiKeyFingerprint(apiKey) !== tokenApiKeyFingerprint || ms >= tokenExpiration
)
Comment thread src/utils/hs256-jwt.mjs
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 2f2adca

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/utils/jwt-token-generator.mjs:63

  • The PR description mentions this HS256 runtime token generator being used for “runtime tokens”, but there are currently no non-test call sites importing/using getToken (repo-wide search only shows this file plus unit tests). If this is intended to be used in production, please wire it up in the relevant runtime code; otherwise consider updating the PR description (or removing dead code/tests if it’s no longer needed).
function getToken(apiKey) {
  if (shouldRegenerateToken(apiKey)) {
    generateToken(apiKey, 86400) // Hard-coded to regenerate the token every 24 hours
  }
  return jwtToken
}

export { getToken }

@PeterDaveHello PeterDaveHello force-pushed the secure-jwt-dependency-cleanup branch from 2f2adca to 59d9ce3 Compare July 2, 2026 21:41
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 59d9ce3

Use a focused HS256 signer backed by @noble/hashes for runtime and
Firefox AMO tokens. This keeps browser builds from carrying a Node
crypto polyfill tree while limiting signing to the algorithm this
project actually uses.

Validate JWT secrets and API key parts before signing so invalid
configuration fails explicitly instead of producing tokens from empty
or coerced values. Pin JWT headers to HS256 while preserving required
custom fields such as ChatGLM's sign_type.

Align runtime token cache expiry with the JWT exp claim to avoid reusing
cached tokens at the expiration boundary. Compare API keys through a
SHA-256 fingerprint so cache invalidation does not retain raw secrets.

Preserve required claims and AMO authorization behavior with tests. Add
coverage for HS256 signatures, token cache behavior, invalid keys,
secret-only API key changes, and AMO signing.

Update uuid for dependency hygiene. Keep pre-commit on its existing 1.x
hook behavior and pin its transitive cross-spawn dependency to the fixed
7.0.6 release through npm overrides.
@PeterDaveHello PeterDaveHello force-pushed the secure-jwt-dependency-cleanup branch from 59d9ce3 to 3816d6e Compare July 4, 2026 10:04
@PeterDaveHello

Copy link
Copy Markdown
Member Author

/agentic_review

@PeterDaveHello PeterDaveHello requested a review from Copilot July 4, 2026 10:29
@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 3816d6e

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Comment on lines +11 to 15
function parseApiKey(apiKey) {
const parts = apiKey.split('.')
if (parts.length !== 2) {
throw new Error('Invalid API key')
}
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.

2 participants