Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ runs:

- uses: actions/setup-node@v4
with:
node-version: "22"
node-version: "24"
cache: "pnpm"

- run: pnpm install --frozen-lockfile
Expand Down
5 changes: 4 additions & 1 deletion packages/did/src/did-resolvers/get-did-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ export function getDidResolver({
}: GetDidResolverOptions = {}): DidResolver {
const keyResolver = getKeyDidResolver()
const webResolver = getWebDidResolver(webOptions)
const jwksResolver = getJwksDidResolver(webOptions)
const jwksResolver = getJwksDidResolver({
...webOptions,
fetch: webOptions.fetch as typeof globalThis.fetch,
})
const pkhResolver = getPkhDidResolver()

const didResolver = new DidResolver(
Expand Down
8 changes: 6 additions & 2 deletions packages/did/src/did-resolvers/web-did-resolver.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ParsedDID } from "did-resolver"

import { beforeEach, describe, expect, it, vi } from "vitest"
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"

import { getResolver } from "./web-did-resolver"

Expand All @@ -14,7 +14,11 @@ describe("web-did-resolver", () => {

beforeEach(() => {
mockFetch.mockReset()
global.fetch = mockFetch
vi.stubGlobal("fetch", mockFetch)
})

afterEach(() => {
vi.unstubAllGlobals()
})

describe("getResolver", () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/did/src/did-resolvers/web-did-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import type {
ParsedDID,
} from "did-resolver"

import type { FetchLike } from "../types"

import {
isDidDocument,
isDidDocumentForDid,
type DidDocument,
} from "../did-document"
import { isDidWebUri } from "../methods/did-web"

type Fetch = typeof globalThis.fetch

export interface DidWebResolverOptions {
/**
* The path to the did.json file.
Expand All @@ -37,7 +37,7 @@ export interface DidWebResolverOptions {
*
* @default globalThis.fetch
*/
fetch?: Fetch
fetch?: FetchLike
/**
* The hosts that are allowed to be used via `http`. All other hosts will
* require `https`.
Expand All @@ -58,7 +58,7 @@ const DEFAULT_DOC_PATH = "/.well-known/did.json"
*/
async function fetchDidDocumentAtUrl(
url: string | URL,
{ fetch = globalThis.fetch }: { fetch?: Fetch } = {},
{ fetch = globalThis.fetch }: { fetch?: FetchLike } = {},
): Promise<DidDocument> {
const res = await fetch(url, { mode: "cors" })

Expand Down
5 changes: 5 additions & 0 deletions packages/did/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { DidDocument } from "./did-document"
import type { DidUri } from "./did-uri"

export type FetchLike = (
input: string | URL | Request,
init?: RequestInit,
) => Promise<Response>

export interface DidUriWithDocument<T extends DidUri = DidUri> {
did: T
didDocument: DidDocument
Expand Down
16 changes: 13 additions & 3 deletions tools/api-utils/src/middleware/signed-payload-validator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ValidationTargets } from "hono"
import type { MiddlewareHandler, ValidationTargets } from "hono"

import { isDidUri, type DidUri, type Resolvable } from "@agentcommercekit/did"
import { isJwtString, type JwtString } from "@agentcommercekit/jwt"
Expand All @@ -8,6 +8,12 @@ import * as v from "valibot"

import { validatePayload } from "../validate-payload"

interface SignedPayloadEnv {
Variables: {
resolver: Resolvable
}
}

interface ValidatedSignedPayload<T> {
issuer: DidUri
body: T
Expand Down Expand Up @@ -38,9 +44,13 @@ const signedPayloadSchema = v.object({
export const signedPayloadValidator = <T>(
target: keyof ValidationTargets,
schema: v.GenericSchema<unknown, T>,
) =>
): MiddlewareHandler<
SignedPayloadEnv,
string,
{ out: { json: ValidatedSignedPayload<T> } }
> =>
validator(target, async (value, c): Promise<ValidatedSignedPayload<T>> => {
const didResolver = c.get("resolver") as Resolvable | undefined
const didResolver = c.get("resolver")
Comment on lines +47 to +53
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a runtime guard for missing resolver to avoid opaque failures.
Even with env typing, a misconfigured context can still yield undefined at runtime; throwing a clear error inside the try preserves the dev-mode bypass path.

🧯 Suggested guard
-  validator(target, async (value, c): Promise<ValidatedSignedPayload<T>> => {
-    const didResolver = c.get("resolver")
-
-    try {
+  validator(target, async (value, c): Promise<ValidatedSignedPayload<T>> => {
+    const didResolver = c.get("resolver")
+
+    try {
+      if (!didResolver) {
+        throw new Error("Missing DID resolver in context")
+      }
       const data = v.parse(signedPayloadSchema, value)
       const { parsed, body } = await validatePayload(
         data.payload,
         schema,
         didResolver,
       )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/api-utils/src/middleware/signed-payload-validator.ts` around lines 47 -
53, Add a runtime guard inside the async validator callback to detect if
c.get("resolver") returns undefined and throw a clear, descriptive Error (e.g.
"missing DID resolver in context") before continuing; specifically, in the
validator(target, async (value, c) => { ... }) block where you call const
didResolver = c.get("resolver"), check didResolver and throw if falsy so you
avoid opaque failures while preserving the existing dev-mode bypass path and
subsequent try/catch flow.


try {
const data = v.parse(signedPayloadSchema, value)
Expand Down