@passportsign/core 0.1.0 → 0.2.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/badge.d.ts +5 -0
- package/dist/badge.d.ts.map +1 -1
- package/dist/badge.js +8 -2
- package/dist/badge.js.map +1 -1
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js +2 -8
- package/dist/bind.js.map +1 -1
- package/dist/bundle-fs.d.ts +16 -0
- package/dist/bundle-fs.d.ts.map +1 -0
- package/dist/bundle-fs.js +31 -0
- package/dist/bundle-fs.js.map +1 -0
- package/dist/bundle.d.ts +13 -5
- package/dist/bundle.d.ts.map +1 -1
- package/dist/bundle.js +18 -20
- package/dist/bundle.js.map +1 -1
- package/dist/canonical.d.ts.map +1 -1
- package/dist/canonical.js +3 -4
- package/dist/canonical.js.map +1 -1
- package/dist/classify.d.ts +68 -0
- package/dist/classify.d.ts.map +1 -0
- package/dist/classify.js +117 -0
- package/dist/classify.js.map +1 -0
- package/dist/dsse-common.d.ts +32 -0
- package/dist/dsse-common.d.ts.map +1 -0
- package/dist/dsse-common.js +26 -0
- package/dist/dsse-common.js.map +1 -0
- package/dist/dsse-web.d.ts +28 -0
- package/dist/dsse-web.d.ts.map +1 -0
- package/dist/dsse-web.js +81 -0
- package/dist/dsse-web.js.map +1 -0
- package/dist/dsse.d.ts +2 -26
- package/dist/dsse.d.ts.map +1 -1
- package/dist/dsse.js +2 -19
- package/dist/dsse.js.map +1 -1
- package/dist/encoding.d.ts +20 -0
- package/dist/encoding.d.ts.map +1 -0
- package/dist/encoding.js +88 -0
- package/dist/encoding.js.map +1 -0
- package/dist/github.js +2 -2
- package/dist/github.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/log/rekor.d.ts +1 -1
- package/dist/log/rekor.d.ts.map +1 -1
- package/dist/log/rekor.js +7 -10
- package/dist/log/rekor.js.map +1 -1
- package/dist/lookup.d.ts +46 -0
- package/dist/lookup.d.ts.map +1 -0
- package/dist/lookup.js +101 -0
- package/dist/lookup.js.map +1 -0
- package/dist/merkle.js +3 -3
- package/dist/merkle.js.map +1 -1
- package/dist/nonce.js +1 -1
- package/dist/nonce.js.map +1 -1
- package/dist/profile-index.d.ts +64 -0
- package/dist/profile-index.d.ts.map +1 -0
- package/dist/profile-index.js +161 -0
- package/dist/profile-index.js.map +1 -0
- package/dist/revoke.d.ts +30 -0
- package/dist/revoke.d.ts.map +1 -0
- package/dist/revoke.js +42 -0
- package/dist/revoke.js.map +1 -0
- package/dist/sdk-payload.d.ts.map +1 -1
- package/dist/sdk-payload.js +4 -6
- package/dist/sdk-payload.js.map +1 -1
- package/dist/statement.d.ts +41 -0
- package/dist/statement.d.ts.map +1 -1
- package/dist/statement.js +43 -0
- package/dist/statement.js.map +1 -1
- package/dist/submit.d.ts +3 -3
- package/dist/submit.d.ts.map +1 -1
- package/dist/submit.js +3 -14
- package/dist/submit.js.map +1 -1
- package/dist/verifier.d.ts.map +1 -1
- package/dist/verifier.js +4 -14
- package/dist/verifier.js.map +1 -1
- package/dist/web.d.ts +35 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +35 -0
- package/dist/web.js.map +1 -0
- package/package.json +6 -2
- package/src/badge.ts +124 -113
- package/src/bind.ts +128 -137
- package/src/bundle-fs.ts +40 -0
- package/src/bundle.ts +138 -127
- package/src/canonical.ts +33 -33
- package/src/classify.ts +165 -0
- package/src/dsse-common.ts +45 -0
- package/src/dsse-web.ts +97 -0
- package/src/dsse.ts +63 -91
- package/src/encoding.ts +96 -0
- package/src/github.ts +196 -196
- package/src/index.ts +59 -2
- package/src/log/rekor.ts +330 -334
- package/src/lookup.ts +175 -0
- package/src/merkle.ts +187 -187
- package/src/nonce.ts +53 -53
- package/src/profile-index.ts +222 -0
- package/src/revoke.ts +67 -0
- package/src/sdk-payload.ts +60 -62
- package/src/statement.ts +203 -119
- package/src/submit.ts +38 -54
- package/src/verifier.ts +304 -317
- package/src/web.ts +175 -0
package/src/github.ts
CHANGED
|
@@ -1,196 +1,196 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub gist control check per spec §3 step 5 and §14 "The gist control check."
|
|
3
|
-
*
|
|
4
|
-
* The honest semantic claim is: at the moment we checked, the named user
|
|
5
|
-
* controlled a public gist with the expected filename and content. We
|
|
6
|
-
* capture `html_url`, `updated_at`, and a SHA-256 of the content so the
|
|
7
|
-
* evidence is independently re-checkable later (e.g. via the Wayback
|
|
8
|
-
* Machine).
|
|
9
|
-
*
|
|
10
|
-
* The optional `token` is **purely for rate-limit headroom** — it
|
|
11
|
-
* carries zero special access. Unauth'd: 60 req/hr; with token: 5000.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import { PassportsignError } from './errors.js';
|
|
16
|
-
|
|
17
|
-
export interface GistEvidence {
|
|
18
|
-
url: string;
|
|
19
|
-
content_sha256: string;
|
|
20
|
-
updated_at: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface CheckGistOptions {
|
|
24
|
-
username: string;
|
|
25
|
-
expected_filename: string;
|
|
26
|
-
expected_content: string;
|
|
27
|
-
not_before: Date;
|
|
28
|
-
token?: string;
|
|
29
|
-
fetch?: typeof fetch;
|
|
30
|
-
baseUrl?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface GistFile {
|
|
34
|
-
filename?: string;
|
|
35
|
-
content?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface GistSummary {
|
|
39
|
-
id: string;
|
|
40
|
-
html_url: string;
|
|
41
|
-
updated_at: string;
|
|
42
|
-
owner?: { login?: string } | null;
|
|
43
|
-
files: Record<string, GistFile>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const DEFAULT_BASE_URL = 'https://api.github.com';
|
|
47
|
-
const GIST_LIST_PER_PAGE = 100;
|
|
48
|
-
|
|
49
|
-
function sha256Hex(content: string): string {
|
|
50
|
-
return
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function authHeaders(token: string | undefined): Record<string, string> {
|
|
54
|
-
return {
|
|
55
|
-
Accept: 'application/vnd.github+json',
|
|
56
|
-
'X-GitHub-Api-Version': '2022-11-28',
|
|
57
|
-
'User-Agent': 'passportsign-cli',
|
|
58
|
-
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export async function checkGistControl(opts: CheckGistOptions): Promise<GistEvidence> {
|
|
63
|
-
const fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
64
|
-
const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;
|
|
65
|
-
const headers = authHeaders(opts.token);
|
|
66
|
-
|
|
67
|
-
if (opts.username.length === 0) {
|
|
68
|
-
throw new PassportsignError('username_invalid', 'username must be non-empty');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Step 1: list the user's gists, filter by filename.
|
|
72
|
-
const listUrl = `${baseUrl}/users/${encodeURIComponent(opts.username)}/gists?per_page=${GIST_LIST_PER_PAGE}`;
|
|
73
|
-
let listResponse: Response;
|
|
74
|
-
try {
|
|
75
|
-
listResponse = await fetchImpl(listUrl, { headers });
|
|
76
|
-
} catch (err) {
|
|
77
|
-
throw new PassportsignError(
|
|
78
|
-
'internal_error',
|
|
79
|
-
`GitHub list-gists request failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
80
|
-
err,
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (listResponse.status === 404) {
|
|
85
|
-
throw new PassportsignError(
|
|
86
|
-
'username_invalid',
|
|
87
|
-
`GitHub user '${opts.username}' not found`,
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
if (!listResponse.ok) {
|
|
91
|
-
throw new PassportsignError(
|
|
92
|
-
'internal_error',
|
|
93
|
-
`GitHub list-gists returned HTTP ${listResponse.status}`,
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
let listBody: unknown;
|
|
98
|
-
try {
|
|
99
|
-
listBody = await listResponse.json();
|
|
100
|
-
} catch (err) {
|
|
101
|
-
throw new PassportsignError(
|
|
102
|
-
'internal_error',
|
|
103
|
-
'GitHub list-gists returned non-JSON',
|
|
104
|
-
err,
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
if (!Array.isArray(listBody)) {
|
|
108
|
-
throw new PassportsignError(
|
|
109
|
-
'internal_error',
|
|
110
|
-
'GitHub list-gists did not return an array',
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const matches = (listBody as GistSummary[])
|
|
115
|
-
.filter((g) => g && typeof g === 'object' && opts.expected_filename in (g.files ?? {}))
|
|
116
|
-
.sort((a, b) => (a.updated_at < b.updated_at ? 1 : -1));
|
|
117
|
-
|
|
118
|
-
const match = matches[0];
|
|
119
|
-
if (!match) {
|
|
120
|
-
throw new PassportsignError(
|
|
121
|
-
'gist_not_found',
|
|
122
|
-
`no public gist owned by '${opts.username}' contains file '${opts.expected_filename}'`,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Step 2: re-fetch the gist by id to get the full content.
|
|
127
|
-
let detailResponse: Response;
|
|
128
|
-
try {
|
|
129
|
-
detailResponse = await fetchImpl(`${baseUrl}/gists/${match.id}`, { headers });
|
|
130
|
-
} catch (err) {
|
|
131
|
-
throw new PassportsignError(
|
|
132
|
-
'internal_error',
|
|
133
|
-
`GitHub get-gist request failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
134
|
-
err,
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
if (!detailResponse.ok) {
|
|
138
|
-
throw new PassportsignError(
|
|
139
|
-
'internal_error',
|
|
140
|
-
`GitHub get-gist returned HTTP ${detailResponse.status}`,
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
let detail: GistSummary;
|
|
145
|
-
try {
|
|
146
|
-
detail = (await detailResponse.json()) as GistSummary;
|
|
147
|
-
} catch (err) {
|
|
148
|
-
throw new PassportsignError('internal_error', 'GitHub get-gist returned non-JSON', err);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Step 3: owner check (case-insensitive per spec §10 row 7).
|
|
152
|
-
const ownerLogin = detail.owner?.login;
|
|
153
|
-
if (!ownerLogin || ownerLogin.toLowerCase() !== opts.username.toLowerCase()) {
|
|
154
|
-
throw new PassportsignError(
|
|
155
|
-
'gist_wrong_owner',
|
|
156
|
-
`gist ${match.id} owner '${ownerLogin ?? 'unknown'}' does not match expected '${opts.username}'`,
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Step 4: content exact match.
|
|
161
|
-
const file = (detail.files ?? {})[opts.expected_filename];
|
|
162
|
-
const content = file?.content;
|
|
163
|
-
if (typeof content !== 'string') {
|
|
164
|
-
throw new PassportsignError(
|
|
165
|
-
'gist_wrong_content',
|
|
166
|
-
`gist ${match.id} has no readable content for '${opts.expected_filename}'`,
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
if (content !== opts.expected_content) {
|
|
170
|
-
throw new PassportsignError(
|
|
171
|
-
'gist_wrong_content',
|
|
172
|
-
`gist ${match.id} content does not exactly match the expected nonce`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Step 5: freshness — gist's updated_at must be at/after init.
|
|
177
|
-
const updatedAtMs = Date.parse(detail.updated_at);
|
|
178
|
-
if (Number.isNaN(updatedAtMs)) {
|
|
179
|
-
throw new PassportsignError(
|
|
180
|
-
'internal_error',
|
|
181
|
-
`gist ${match.id} updated_at is unparseable: ${detail.updated_at}`,
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
if (updatedAtMs < opts.not_before.getTime()) {
|
|
185
|
-
throw new PassportsignError(
|
|
186
|
-
'gist_predates_init',
|
|
187
|
-
`gist ${match.id} updated_at (${detail.updated_at}) predates init (${opts.not_before.toISOString()})`,
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
url: detail.html_url,
|
|
193
|
-
content_sha256: sha256Hex(content),
|
|
194
|
-
updated_at: detail.updated_at,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* GitHub gist control check per spec §3 step 5 and §14 "The gist control check."
|
|
3
|
+
*
|
|
4
|
+
* The honest semantic claim is: at the moment we checked, the named user
|
|
5
|
+
* controlled a public gist with the expected filename and content. We
|
|
6
|
+
* capture `html_url`, `updated_at`, and a SHA-256 of the content so the
|
|
7
|
+
* evidence is independently re-checkable later (e.g. via the Wayback
|
|
8
|
+
* Machine).
|
|
9
|
+
*
|
|
10
|
+
* The optional `token` is **purely for rate-limit headroom** — it
|
|
11
|
+
* carries zero special access. Unauth'd: 60 req/hr; with token: 5000.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { sha256Hex as sha256HexOfBytes, utf8ToBytes } from './encoding.js';
|
|
15
|
+
import { PassportsignError } from './errors.js';
|
|
16
|
+
|
|
17
|
+
export interface GistEvidence {
|
|
18
|
+
url: string;
|
|
19
|
+
content_sha256: string;
|
|
20
|
+
updated_at: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CheckGistOptions {
|
|
24
|
+
username: string;
|
|
25
|
+
expected_filename: string;
|
|
26
|
+
expected_content: string;
|
|
27
|
+
not_before: Date;
|
|
28
|
+
token?: string;
|
|
29
|
+
fetch?: typeof fetch;
|
|
30
|
+
baseUrl?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface GistFile {
|
|
34
|
+
filename?: string;
|
|
35
|
+
content?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface GistSummary {
|
|
39
|
+
id: string;
|
|
40
|
+
html_url: string;
|
|
41
|
+
updated_at: string;
|
|
42
|
+
owner?: { login?: string } | null;
|
|
43
|
+
files: Record<string, GistFile>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const DEFAULT_BASE_URL = 'https://api.github.com';
|
|
47
|
+
const GIST_LIST_PER_PAGE = 100;
|
|
48
|
+
|
|
49
|
+
function sha256Hex(content: string): string {
|
|
50
|
+
return sha256HexOfBytes(utf8ToBytes(content));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function authHeaders(token: string | undefined): Record<string, string> {
|
|
54
|
+
return {
|
|
55
|
+
Accept: 'application/vnd.github+json',
|
|
56
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
57
|
+
'User-Agent': 'passportsign-cli',
|
|
58
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function checkGistControl(opts: CheckGistOptions): Promise<GistEvidence> {
|
|
63
|
+
const fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
64
|
+
const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;
|
|
65
|
+
const headers = authHeaders(opts.token);
|
|
66
|
+
|
|
67
|
+
if (opts.username.length === 0) {
|
|
68
|
+
throw new PassportsignError('username_invalid', 'username must be non-empty');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Step 1: list the user's gists, filter by filename.
|
|
72
|
+
const listUrl = `${baseUrl}/users/${encodeURIComponent(opts.username)}/gists?per_page=${GIST_LIST_PER_PAGE}`;
|
|
73
|
+
let listResponse: Response;
|
|
74
|
+
try {
|
|
75
|
+
listResponse = await fetchImpl(listUrl, { headers });
|
|
76
|
+
} catch (err) {
|
|
77
|
+
throw new PassportsignError(
|
|
78
|
+
'internal_error',
|
|
79
|
+
`GitHub list-gists request failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
80
|
+
err,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (listResponse.status === 404) {
|
|
85
|
+
throw new PassportsignError(
|
|
86
|
+
'username_invalid',
|
|
87
|
+
`GitHub user '${opts.username}' not found`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (!listResponse.ok) {
|
|
91
|
+
throw new PassportsignError(
|
|
92
|
+
'internal_error',
|
|
93
|
+
`GitHub list-gists returned HTTP ${listResponse.status}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let listBody: unknown;
|
|
98
|
+
try {
|
|
99
|
+
listBody = await listResponse.json();
|
|
100
|
+
} catch (err) {
|
|
101
|
+
throw new PassportsignError(
|
|
102
|
+
'internal_error',
|
|
103
|
+
'GitHub list-gists returned non-JSON',
|
|
104
|
+
err,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
if (!Array.isArray(listBody)) {
|
|
108
|
+
throw new PassportsignError(
|
|
109
|
+
'internal_error',
|
|
110
|
+
'GitHub list-gists did not return an array',
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const matches = (listBody as GistSummary[])
|
|
115
|
+
.filter((g) => g && typeof g === 'object' && opts.expected_filename in (g.files ?? {}))
|
|
116
|
+
.sort((a, b) => (a.updated_at < b.updated_at ? 1 : -1));
|
|
117
|
+
|
|
118
|
+
const match = matches[0];
|
|
119
|
+
if (!match) {
|
|
120
|
+
throw new PassportsignError(
|
|
121
|
+
'gist_not_found',
|
|
122
|
+
`no public gist owned by '${opts.username}' contains file '${opts.expected_filename}'`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Step 2: re-fetch the gist by id to get the full content.
|
|
127
|
+
let detailResponse: Response;
|
|
128
|
+
try {
|
|
129
|
+
detailResponse = await fetchImpl(`${baseUrl}/gists/${match.id}`, { headers });
|
|
130
|
+
} catch (err) {
|
|
131
|
+
throw new PassportsignError(
|
|
132
|
+
'internal_error',
|
|
133
|
+
`GitHub get-gist request failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
134
|
+
err,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
if (!detailResponse.ok) {
|
|
138
|
+
throw new PassportsignError(
|
|
139
|
+
'internal_error',
|
|
140
|
+
`GitHub get-gist returned HTTP ${detailResponse.status}`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let detail: GistSummary;
|
|
145
|
+
try {
|
|
146
|
+
detail = (await detailResponse.json()) as GistSummary;
|
|
147
|
+
} catch (err) {
|
|
148
|
+
throw new PassportsignError('internal_error', 'GitHub get-gist returned non-JSON', err);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Step 3: owner check (case-insensitive per spec §10 row 7).
|
|
152
|
+
const ownerLogin = detail.owner?.login;
|
|
153
|
+
if (!ownerLogin || ownerLogin.toLowerCase() !== opts.username.toLowerCase()) {
|
|
154
|
+
throw new PassportsignError(
|
|
155
|
+
'gist_wrong_owner',
|
|
156
|
+
`gist ${match.id} owner '${ownerLogin ?? 'unknown'}' does not match expected '${opts.username}'`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Step 4: content exact match.
|
|
161
|
+
const file = (detail.files ?? {})[opts.expected_filename];
|
|
162
|
+
const content = file?.content;
|
|
163
|
+
if (typeof content !== 'string') {
|
|
164
|
+
throw new PassportsignError(
|
|
165
|
+
'gist_wrong_content',
|
|
166
|
+
`gist ${match.id} has no readable content for '${opts.expected_filename}'`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
if (content !== opts.expected_content) {
|
|
170
|
+
throw new PassportsignError(
|
|
171
|
+
'gist_wrong_content',
|
|
172
|
+
`gist ${match.id} content does not exactly match the expected nonce`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Step 5: freshness — gist's updated_at must be at/after init.
|
|
177
|
+
const updatedAtMs = Date.parse(detail.updated_at);
|
|
178
|
+
if (Number.isNaN(updatedAtMs)) {
|
|
179
|
+
throw new PassportsignError(
|
|
180
|
+
'internal_error',
|
|
181
|
+
`gist ${match.id} updated_at is unparseable: ${detail.updated_at}`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
if (updatedAtMs < opts.not_before.getTime()) {
|
|
185
|
+
throw new PassportsignError(
|
|
186
|
+
'gist_predates_init',
|
|
187
|
+
`gist ${match.id} updated_at (${detail.updated_at}) predates init (${opts.not_before.toISOString()})`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
url: detail.html_url,
|
|
193
|
+
content_sha256: sha256Hex(content),
|
|
194
|
+
updated_at: detail.updated_at,
|
|
195
|
+
};
|
|
196
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -14,23 +14,29 @@ export {
|
|
|
14
14
|
export {
|
|
15
15
|
IN_TOTO_STATEMENT_TYPE,
|
|
16
16
|
PASSPORTSIGN_PREDICATE_TYPE,
|
|
17
|
+
PASSPORTSIGN_REVOCATION_PREDICATE_TYPE,
|
|
18
|
+
buildRevocationStatement,
|
|
17
19
|
buildStatement,
|
|
20
|
+
type BuildRevocationStatementInput,
|
|
18
21
|
type BuildStatementInput,
|
|
19
22
|
type DisclosureLevel,
|
|
20
23
|
type PassportsignPredicate,
|
|
24
|
+
type PassportsignRevocationPredicate,
|
|
25
|
+
type PassportsignRevocationStatement,
|
|
21
26
|
type PassportsignStatement,
|
|
22
27
|
} from './statement.js';
|
|
23
28
|
|
|
24
29
|
export {
|
|
25
30
|
BUNDLE_FORMAT_VERSION,
|
|
26
31
|
BundleValidationError,
|
|
27
|
-
|
|
32
|
+
assembleBundle,
|
|
28
33
|
validateBundle,
|
|
29
|
-
writeBundle,
|
|
30
34
|
type PassportsignBundle,
|
|
31
35
|
type RekorBundleFields,
|
|
32
36
|
} from './bundle.js';
|
|
33
37
|
|
|
38
|
+
export { readBundle, writeBundle } from './bundle-fs.js';
|
|
39
|
+
|
|
34
40
|
export {
|
|
35
41
|
ERROR_CODES,
|
|
36
42
|
PassportsignError,
|
|
@@ -74,6 +80,12 @@ export {
|
|
|
74
80
|
type SignEnvelopeResult,
|
|
75
81
|
} from './dsse.js';
|
|
76
82
|
|
|
83
|
+
export {
|
|
84
|
+
p1363ToDer,
|
|
85
|
+
signEnvelopeWeb,
|
|
86
|
+
type SignEnvelopeWebResult,
|
|
87
|
+
} from './dsse-web.js';
|
|
88
|
+
|
|
77
89
|
export {
|
|
78
90
|
DEFAULT_REKOR_BASE_URL,
|
|
79
91
|
PublicSigstoreRekorClient,
|
|
@@ -88,8 +100,15 @@ export {
|
|
|
88
100
|
submitBinding,
|
|
89
101
|
type SubmitBindingDeps,
|
|
90
102
|
type SubmitBindingResult,
|
|
103
|
+
type SubmittableStatement,
|
|
91
104
|
} from './submit.js';
|
|
92
105
|
|
|
106
|
+
export {
|
|
107
|
+
prepareRevocation,
|
|
108
|
+
type PrepareRevocationInput,
|
|
109
|
+
type PreparedRevocation,
|
|
110
|
+
} from './revoke.js';
|
|
111
|
+
|
|
93
112
|
export {
|
|
94
113
|
hashLeaf,
|
|
95
114
|
hashPair,
|
|
@@ -110,6 +129,44 @@ export {
|
|
|
110
129
|
type BadgeInput,
|
|
111
130
|
} from './badge.js';
|
|
112
131
|
|
|
132
|
+
export {
|
|
133
|
+
PROFILE_INDEX_FILENAME,
|
|
134
|
+
PROFILE_INDEX_VERSION,
|
|
135
|
+
ProfileIndexValidationError,
|
|
136
|
+
addBinding,
|
|
137
|
+
addRevocation,
|
|
138
|
+
createProfileIndex,
|
|
139
|
+
fetchProfileIndex,
|
|
140
|
+
mergeProfileIndexes,
|
|
141
|
+
profileIndexUrl,
|
|
142
|
+
validateProfileIndex,
|
|
143
|
+
type FetchProfileIndexOptions,
|
|
144
|
+
type ProfileIndex,
|
|
145
|
+
type ProfileIndexBinding,
|
|
146
|
+
type ProfileIndexRevocation,
|
|
147
|
+
} from './profile-index.js';
|
|
148
|
+
|
|
149
|
+
export {
|
|
150
|
+
EntryParseError,
|
|
151
|
+
STALENESS_WINDOW_MS,
|
|
152
|
+
classifyBindings,
|
|
153
|
+
parseIntotoEntry,
|
|
154
|
+
type BindingState,
|
|
155
|
+
type ClassifiedBinding,
|
|
156
|
+
type ClassifyBindingsInput,
|
|
157
|
+
type InTotoStatement,
|
|
158
|
+
type ParsedIntotoEntry,
|
|
159
|
+
} from './classify.js';
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
lookupBindings,
|
|
163
|
+
lookupFromIndex,
|
|
164
|
+
type LookupBindingsDeps,
|
|
165
|
+
type LookupDeps,
|
|
166
|
+
type LookupEntryProblem,
|
|
167
|
+
type LookupResult,
|
|
168
|
+
} from './lookup.js';
|
|
169
|
+
|
|
113
170
|
export {
|
|
114
171
|
verifyBundle,
|
|
115
172
|
type BundleVerifyResult,
|