{"id":"GHSA-jfc7-64v2-mr8c","revision":"b6cc9bab8f67438ca169d26fc0ec7ef4","records":[{"revision":"b6cc9bab8f67438ca169d26fc0ec7ef4","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-jfc7-64v2-mr8c","aliases":["CVE-2026-48758"],"title":"@sigstore/core has DSSE payloadType type-binding failure","description":"### Impact\nThe `preAuthEncoding` function in `@sigstore/core` uses Node.js `'ascii'` encoding when converting the PAE (Pre-Authentication Encoding) string to bytes. This allows `payloadType` to be mutated after signing without invalidating the signature, breaking the type-binding guarantee that DSSE is designed to provide.\n\nIn `packages/core/src/dsse.ts`, the PAE function builds a string containing `payloadType` and then encodes it with `Buffer.from(prefix, 'ascii')`.\n\nIn Node.js, `'ascii'` encoding for string-to-Buffer is equivalent to `'latin1'`, which **truncates characters above U+00FF to their low byte**. This means for any ASCII character, there exist Unicode characters (at U+01xx, U+02xx, etc.) that produce the identical encoded byte:\n\n| Original | Codepoint | Mutant | Codepoint | Encoded byte |\n|----------|-----------|--------|-----------|--------------|\n| `t`      | U+0074    | `Ŵ`    | U+0174    | `0x74`       |\n| `e`      | U+0065    | `ť`    | U+0165    | `0x65`       |\n\nAn attacker can substitute every character in `payloadType` with a Unicode variant whose low byte matches, producing **identical PAE bytes** and a passing signature verification.\n\nAdditionally, `payloadType.length` returns the JavaScript string length (UTF-16 code units) rather than the UTF-8 byte length required by the DSSE spec, though this is only a contributing factor for non-ASCII types.\n\n#### Reproduction\n\n```javascript\nconst { preAuthEncoding } = require('@sigstore/core/dist/dsse.js');\nconst payload = Buffer.from('hello world');\n\nconst original = preAuthEncoding('text/plain', payload);\n// U+01xx chars whose low bytes match the original ASCII chars\nconst mutant = preAuthEncoding('\\u0174\\u0165\\u0178\\u0174/\\u0170\\u016c\\u0161\\u0169\\u016e', payload);\n\nconsole.log('PAE bytes equal:', original.equals(mutant)); // true — should be false\n```","state":0,"publishedAt":"2026-06-26T19:11:19+00:00","modifiedAt":"2026-09-10T03:51:08.7862074+00:00","affected":[{"ecosystem":"npm","name":"@sigstore/core","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"3.2.1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/sigstore/sigstore-js/security/advisories/GHSA-jfc7-64v2-mr8c","https://github.com/sigstore/sigstore-js"],"sourceUrl":"https://github.com/advisories/GHSA-jfc7-64v2-mr8c","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-jfc7-64v2-mr8c","identifiers":["GHSA-jfc7-64v2-mr8c","CVE-2026-48758"]},"retained":false,"historicalAffected":[]}],"coverage":[]}