Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
108 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { type BrokeredSecret, fill, misdirectedNote, newlyMisdirected, scrub, scrubHeaders, scrubbable, standInsIn } from "./broker.ts";
5
6const NPM: BrokeredSecret = {
7 standIn: `g1tsec_${"a".repeat(48)}`,
8 name: "NPM_TOKEN",
9 value: "npm_realvalue123",
10 hosts: ["registry.npmjs.org"],
11};
12const API: BrokeredSecret = {
13 standIn: `g1tsec_${"b".repeat(48)}`,
14 name: "API_KEY",
15 value: "key with spaces&=",
16 hosts: ["*.example.com"],
17};
18const secrets = [NPM, API];
19
20const basic = (text: string) => `Basic ${btoa(text)}`;
21
22test("stand-ins are found in the URL, the headers and Basic credentials", () => {
23 assert.deepEqual(standInsIn(`https://x.example.com/?key=${API.standIn}`, new Headers()), [API.standIn]);
24 assert.deepEqual(standInsIn("https://registry.npmjs.org/", new Headers({ authorization: `Bearer ${NPM.standIn}` })), [NPM.standIn]);
25 assert.deepEqual(standInsIn("https://registry.npmjs.org/", new Headers({ authorization: basic(`me:${NPM.standIn}`) })), [NPM.standIn]);
26 assert.deepEqual(standInsIn("https://registry.npmjs.org/", new Headers({ authorization: "Bearer g1tsec_short" })), []);
27});
28
29test("a stand-in in a header is filled in for its host", () => {
30 const headers = new Headers({ authorization: `Bearer ${NPM.standIn}`, "x-other": "kept" });
31 const out = fill("https://registry.npmjs.org/pkg", headers, secrets);
32 assert.equal(out.headers.get("authorization"), "Bearer npm_realvalue123");
33 assert.equal(out.headers.get("x-other"), "kept");
34 assert.deepEqual(out.filled, [NPM]);
35 assert.deepEqual(out.misdirected, []);
36});
37
38test("custom headers and token schemes are filled in as well", () => {
39 const headers = new Headers({ authorization: `token ${API.standIn}`, "x-api-key": API.standIn });
40 const out = fill("https://api.example.com/v1", headers, secrets);
41 assert.equal(out.headers.get("authorization"), `token ${API.value}`);
42 assert.equal(out.headers.get("x-api-key"), API.value);
43});
44
45test("Basic credentials are decoded, filled in and encoded again", () => {
46 const headers = new Headers({ authorization: basic(`me:${NPM.standIn}`) });
47 const out = fill("https://registry.npmjs.org/", headers, secrets);
48 assert.equal(out.headers.get("authorization"), basic("me:npm_realvalue123"));
49});
50
51test("a stand-in in the query string is filled in, encoded", () => {
52 const out = fill(`https://api.example.com/v1?key=${API.standIn}&q=1`, new Headers(), secrets);
53 assert.equal(new URL(out.url).searchParams.get("key"), API.value);
54 assert.equal(new URL(out.url).searchParams.get("q"), "1");
55 assert.ok(!out.url.includes(API.standIn));
56});
57
58test("a stand-in sent to another host is left as it is and reported", () => {
59 const headers = new Headers({ authorization: `Bearer ${NPM.standIn}` });
60 const out = fill("https://evil.com/steal", headers, secrets);
61 assert.equal(out.headers.get("authorization"), `Bearer ${NPM.standIn}`);
62 assert.deepEqual(out.filled, []);
63 assert.deepEqual(out.misdirected, [NPM]);
64 // Not even a host that only looks like it.
65 assert.deepEqual(fill("https://registry.npmjs.org.evil.com/", headers, secrets).filled, []);
66 assert.deepEqual(fill("https://example.com/", new Headers({ "x-api-key": API.standIn }), secrets).filled, []);
67});
68
69test("a value is filled in only over HTTPS, even for its own host", () => {
70 const headers = new Headers({ authorization: `Bearer ${NPM.standIn}` });
71 const plain = fill("http://registry.npmjs.org/pkg", headers, secrets);
72 assert.equal(plain.headers.get("authorization"), `Bearer ${NPM.standIn}`);
73 assert.deepEqual(plain.filled, []);
74 assert.deepEqual(plain.misdirected, [NPM]);
75});
76
77test("a stand-in the job was not given is left alone, and not reported", () => {
78 const stranger = `g1tsec_${"c".repeat(48)}`;
79 const out = fill("https://registry.npmjs.org/", new Headers({ authorization: `Bearer ${stranger}` }), secrets);
80 assert.equal(out.headers.get("authorization"), `Bearer ${stranger}`);
81 assert.deepEqual(out.filled, []);
82 assert.deepEqual(out.misdirected, []);
83});
84
85test("a value echoed back is swapped for its stand-in again", () => {
86 const headers = scrubHeaders(new Headers({ "x-echo": "Bearer npm_realvalue123", "content-type": "application/json" }), [NPM]);
87 assert.equal(headers.get("x-echo"), `Bearer ${NPM.standIn}`);
88 assert.equal(scrub('{"token":"npm_realvalue123"}', [NPM]), `{"token":"${NPM.standIn}"}`);
89});
90
91test("only small, plain, uncompressed answers have their bodies rewritten", () => {
92 assert.equal(scrubbable(new Headers({ "content-type": "application/json", "content-length": "120" })), true);
93 assert.equal(scrubbable(new Headers({ "content-type": "text/plain; charset=utf-8", "content-length": "10" })), true);
94 assert.equal(scrubbable(new Headers({ "content-type": "application/vnd.api+json", "content-length": "10" })), true);
95 assert.equal(scrubbable(new Headers({ "content-type": "application/octet-stream", "content-length": "10" })), false);
96 assert.equal(scrubbable(new Headers({ "content-type": "application/json", "content-length": "10", "content-encoding": "gzip" })), false);
97 assert.equal(scrubbable(new Headers({ "content-type": "application/json" })), false);
98 assert.equal(scrubbable(new Headers({ "content-type": "application/json", "content-length": String(10 * 1024 * 1024) })), false);
99});
100
101test("a misdirected secret is noted once per host", () => {
102 const first = newlyMisdirected([], "NPM_TOKEN", "Evil.com:443");
103 assert.ok(first);
104 assert.equal(first.note, misdirectedNote("NPM_TOKEN", "evil.com"));
105 assert.match(first.note, /^##\[warning\]NPM_TOKEN was sent to evil\.com, which it is not for/);
106 assert.equal(newlyMisdirected(first.seen, "NPM_TOKEN", "evil.com"), null);
107 assert.ok(newlyMisdirected(first.seen, "NPM_TOKEN", "other.com"));
108});