Skip to content

Commit 3dd95d0

Browse files
committed
fix(trusted-publishing): uri encode the package name for the token exchange request
for #958
1 parent c80ecb0 commit 3dd95d0

File tree

3 files changed

+39
-20
lines changed

3 files changed

+39
-20
lines changed

lib/trusted-publishing/token-exchange.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { OFFICIAL_REGISTRY } from "../definitions/constants.js";
22

33
export default async function tokenExchange(pkg) {
4-
const response = await fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${pkg.name}`, { method: 'POST' });
4+
const response = await fetch(
5+
`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(pkg.name)}`,
6+
{ method: "POST" }
7+
);
58

69
if (response.ok) {
710
return (await response.json()).token;

test/trusted-publishing/oidc-context.test.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,25 @@ test.afterEach.always((t) => {
1919
td.reset();
2020
});
2121

22-
test.serial("that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => {
23-
td.when(trustedCiProvider()).thenResolve(true);
24-
td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 }));
25-
td.when(tokenExchange(pkg)).thenResolve('token-value');
26-
27-
t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg));
28-
});
29-
30-
test.serial("that `false` is returned when the official registry is targeted, but outside the context of a supported CI provider", async (t) => {
31-
td.when(trustedCiProvider()).thenResolve(false);
32-
33-
t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg));
34-
});
22+
test.serial(
23+
"that `true` is returned when a trusted-publishing context has been established with the official registry",
24+
async (t) => {
25+
td.when(trustedCiProvider()).thenResolve(true);
26+
td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 }));
27+
td.when(tokenExchange(pkg)).thenResolve("token-value");
28+
29+
t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg));
30+
}
31+
);
32+
33+
test.serial(
34+
"that `false` is returned when the official registry is targeted, but outside the context of a supported CI provider",
35+
async (t) => {
36+
td.when(trustedCiProvider()).thenResolve(false);
37+
38+
t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg));
39+
}
40+
);
3541

3642
test.serial("that `false` is returned when OIDC token exchange fails in a supported CI provider", async (t) => {
3743
td.when(trustedCiProvider()).thenResolve(true);
Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import test from "ava";
22
import * as td from "testdouble";
33

4-
import tokenExchange from '../../lib/trusted-publishing/token-exchange.js';
4+
import tokenExchange from "../../lib/trusted-publishing/token-exchange.js";
55
import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js";
66

77
// https://api-docs.npmjs.com/#tag/registry.npmjs.org/operation/exchangeOidcToken
88

9-
const packageName = "some-package";
9+
const packageName = "@scope/some-package";
1010
const pkg = { name: packageName };
1111

1212
test.beforeEach(async (t) => {
@@ -19,15 +19,25 @@ test.afterEach.always((t) => {
1919

2020
test.serial("that an access token is returned when token exchange succeeds", async (t) => {
2121
const token = "token-value";
22-
td.when(fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${packageName}`, {method: 'POST'}))
23-
.thenResolve(new Response(JSON.stringify({token}), {status: 201, headers: {'Content-Type': 'application/json'}}));
22+
td.when(
23+
fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, {
24+
method: "POST",
25+
})
26+
).thenResolve(
27+
new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } })
28+
);
2429

2530
t.is(await tokenExchange(pkg), token);
2631
});
2732

2833
test.serial("that `undefined` is returned when token exchange fails", async (t) => {
29-
td.when(fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${packageName}`, {method: 'POST'}))
30-
.thenResolve(new Response(JSON.stringify({message: 'foo'}), {status: 401, headers: {'Content-Type': 'application/json'}}));
34+
td.when(
35+
fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, {
36+
method: "POST",
37+
})
38+
).thenResolve(
39+
new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } })
40+
);
3141

3242
t.is(await tokenExchange(pkg), undefined);
3343
});

0 commit comments

Comments
 (0)