@ggui-ai/gadget-signing 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -338,4 +338,29 @@ export declare function verifyBundleSigstore(input: VerifyBundleSigstoreInput):
338
338
  * a verification.
339
339
  */
340
340
  export declare function extractSigstoreLeafCertPem(signature: SigstoreSignature): string | undefined;
341
+ /**
342
+ * Extract EVERY signer identity — the embedded Fulcio cert's URI and
343
+ * rfc822 (email) `subjectAlternativeName` values — from a
344
+ * {@link SigstoreSignature}'s serialized cosign bundle. Returns an
345
+ * EMPTY array when the bundle is malformed, carries no certificate,
346
+ * or the cert has no URI/email SAN.
347
+ *
348
+ * ALL SANs, deliberately: a certificate can carry BOTH a URI and an
349
+ * email SAN, and a policy that inspected only the first would
350
+ * dead-end on such certs. Callers match their policy against every
351
+ * returned identity and accept on ANY hit.
352
+ *
353
+ * This is a PROJECTION, not a verification: callers that make an
354
+ * authorization decision on the returned identities (e.g. a
355
+ * registry's publish-time identity binding) MUST pair it with
356
+ * {@link verifyBundleSigstore} over the same signature — verification
357
+ * is what proves the SAN-bearing cert is genuinely CA-issued and tied
358
+ * to the signed bytes. Parsing delegates to the same X.509 parser the
359
+ * verifier uses (see {@link extractSANsFromBundle}), so the identities
360
+ * a policy check sees can never drift from what verification enforces.
361
+ *
362
+ * Lives here — next to {@link extractSigstoreLeafCertPem} — so cosign
363
+ * bundle-format knowledge has ONE home.
364
+ */
365
+ export declare function extractSigstoreSANs(signature: SigstoreSignature): readonly string[];
341
366
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -422,19 +422,22 @@ export async function verifyBundleSigstore(input) {
422
422
  }
423
423
  // 3. RegExp identity pre-check. Upstream `sigstore.verify` accepts
424
424
  // only literal-equality identity strings, so a RegExp expectation
425
- // must be enforced here against the bundle's embedded SAN.
425
+ // must be enforced here against the bundle's embedded SANs — ANY
426
+ // matching SAN satisfies the pattern (a cert can carry both a URI
427
+ // and an email SAN).
426
428
  if (expectedIdentity && expectedIdentity.subject instanceof RegExp) {
427
- const san = extractSANFromBundle(parsedBundle);
428
- if (san === undefined) {
429
+ const sans = extractSANsFromBundle(parsedBundle);
430
+ if (sans.length === 0) {
429
431
  return {
430
432
  valid: false,
431
433
  reason: "expectedIdentity.subject is RegExp but bundle has no subjectAlternativeName to match against",
432
434
  };
433
435
  }
434
- if (!expectedIdentity.subject.test(san)) {
436
+ const pattern = expectedIdentity.subject;
437
+ if (!sans.some((san) => pattern.test(san))) {
435
438
  return {
436
439
  valid: false,
437
- reason: `identity mismatch: bundle SAN '${san}' does not match expected pattern ${expectedIdentity.subject}`,
440
+ reason: `identity mismatch: no bundle SAN (${sans.map((s) => `'${s}'`).join(', ')}) matches expected pattern ${pattern}`,
438
441
  };
439
442
  }
440
443
  }
@@ -547,25 +550,62 @@ export function extractSigstoreLeafCertPem(signature) {
547
550
  return rawBytes;
548
551
  }
549
552
  /**
550
- * Reach into a serialized sigstore Bundle and pull the
551
- * `subjectAlternativeName` from the embedded X.509 cert (if any). Used
552
- * by the RegExp-identity pre-check; production verification still goes
553
+ * Extract EVERY signer identity — the embedded Fulcio cert's URI and
554
+ * rfc822 (email) `subjectAlternativeName` values — from a
555
+ * {@link SigstoreSignature}'s serialized cosign bundle. Returns an
556
+ * EMPTY array when the bundle is malformed, carries no certificate,
557
+ * or the cert has no URI/email SAN.
558
+ *
559
+ * ALL SANs, deliberately: a certificate can carry BOTH a URI and an
560
+ * email SAN, and a policy that inspected only the first would
561
+ * dead-end on such certs. Callers match their policy against every
562
+ * returned identity and accept on ANY hit.
563
+ *
564
+ * This is a PROJECTION, not a verification: callers that make an
565
+ * authorization decision on the returned identities (e.g. a
566
+ * registry's publish-time identity binding) MUST pair it with
567
+ * {@link verifyBundleSigstore} over the same signature — verification
568
+ * is what proves the SAN-bearing cert is genuinely CA-issued and tied
569
+ * to the signed bytes. Parsing delegates to the same X.509 parser the
570
+ * verifier uses (see {@link extractSANsFromBundle}), so the identities
571
+ * a policy check sees can never drift from what verification enforces.
572
+ *
573
+ * Lives here — next to {@link extractSigstoreLeafCertPem} — so cosign
574
+ * bundle-format knowledge has ONE home.
575
+ */
576
+ export function extractSigstoreSANs(signature) {
577
+ let parsed;
578
+ try {
579
+ parsed = JSON.parse(signature.bundle);
580
+ }
581
+ catch {
582
+ return [];
583
+ }
584
+ if (parsed === null || typeof parsed !== "object")
585
+ return [];
586
+ return extractSANsFromBundle(parsed);
587
+ }
588
+ /**
589
+ * Reach into a serialized sigstore Bundle and pull every URI/email
590
+ * `subjectAlternativeName` from the embedded X.509 cert (if any).
591
+ * Used by the RegExp-identity pre-check and
592
+ * {@link extractSigstoreSANs}; production verification still goes
553
593
  * through the upstream verifier for the actual cryptographic check.
554
594
  *
555
595
  * Parsing delegates to `@sigstore/core`'s `X509Certificate` — the
556
- * SAME parser the upstream verifier uses — so the SAN this pre-check
557
- * sees can never drift from the SAN verification enforces. (A prior
596
+ * SAME parser the upstream verifier uses — so the SANs this pre-check
597
+ * sees can never drift from the SANs verification enforces. (A prior
558
598
  * hand-rolled DER scan over-captured on real certificates —
559
599
  * discovered on the first genuinely Fulcio-issued cert it ever saw,
560
600
  * 2026-08-10 — and was replaced wholesale rather than patched.)
561
601
  *
562
- * Returns `undefined` if the bundle has no cert (e.g. publicKey-only
602
+ * Returns `[]` if the bundle has no cert (e.g. publicKey-only
563
603
  * bundle), the cert fails to parse, or it carries no URI/email SAN.
564
604
  */
565
- function extractSANFromBundle(bundle) {
605
+ function extractSANsFromBundle(bundle) {
566
606
  const material = bundle.verificationMaterial;
567
607
  if (!material)
568
- return undefined;
608
+ return [];
569
609
  let certB64;
570
610
  if ("certificate" in material && material.certificate) {
571
611
  certB64 = material.certificate.rawBytes;
@@ -576,15 +616,22 @@ function extractSANFromBundle(bundle) {
576
616
  certB64 = material.x509CertificateChain.certificates[0]?.rawBytes;
577
617
  }
578
618
  if (!certB64)
579
- return undefined;
619
+ return [];
580
620
  try {
581
621
  const cert = X509Certificate.parse(Buffer.from(certB64, "base64"));
582
- // `subjectAltName` is the URI GeneralName when present, else the
583
- // rfc822Name — the exact preference order the verifier's identity
584
- // policy uses.
585
- return cert.subjectAltName;
622
+ // Collect BOTH GeneralName kinds the identity policies use — URI
623
+ // first (matching upstream's preference order), then rfc822.
624
+ const ext = cert.extSubjectAltName;
625
+ if (ext === undefined)
626
+ return [];
627
+ const sans = [];
628
+ if (ext.uri !== undefined)
629
+ sans.push(ext.uri);
630
+ if (ext.rfc822Name !== undefined)
631
+ sans.push(ext.rfc822Name);
632
+ return sans;
586
633
  }
587
634
  catch {
588
- return undefined;
635
+ return [];
589
636
  }
590
637
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/gadget-signing",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Gadget bundle signing + verification for the ggui gadget marketplace. Ed25519 author-key path + sigstore/cosign keyless path. Pure-TS @noble crypto for Ed25519 — browser-safe.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [