Skip to content

Commit 59355e3

Browse files
chore(deps): apply weekly update fixes
1 parent 591aa66 commit 59355e3

6 files changed

Lines changed: 1091 additions & 318 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
const GITHUB_ORIGIN = 'https://github.com'
2+
3+
export function integrityValue(integrity: unknown): string {
4+
if (typeof integrity === 'object' && integrity !== null) {
5+
const value = (integrity as { readonly value?: unknown | undefined }).value
6+
return typeof value === 'string' ? value : ''
7+
}
8+
return typeof integrity === 'string' ? integrity : ''
9+
}
10+
11+
export function integrityProvenance(integrity: unknown): {
12+
readonly src: string
13+
readonly date: string
14+
} {
15+
if (typeof integrity === 'object' && integrity !== null) {
16+
const record = integrity as {
17+
readonly src?: unknown | undefined
18+
readonly date?: unknown | undefined
19+
}
20+
return {
21+
__proto__: null,
22+
src: typeof record.src === 'string' ? record.src : '',
23+
date: typeof record.date === 'string' ? record.date : '',
24+
} as { readonly src: string; readonly date: string }
25+
}
26+
return { __proto__: null, src: '', date: '' } as {
27+
readonly src: string
28+
readonly date: string
29+
}
30+
}
31+
32+
function safeReleaseSegment(value: unknown, label: string): string {
33+
if (
34+
typeof value !== 'string' ||
35+
value.length === 0 ||
36+
value === '.' ||
37+
value === '..' ||
38+
/[/\\?#\u0000-\u0020]/u.test(value)
39+
) {
40+
throw new Error(
41+
`external-tools.json ${label} is not a safe GitHub release path segment`,
42+
)
43+
}
44+
return value
45+
}
46+
47+
function githubRepositorySlug(repository: unknown): string {
48+
if (typeof repository !== 'string' || !repository.startsWith('github:')) {
49+
throw new Error(
50+
'external-tools.json repository is not a github:owner/repo reference',
51+
)
52+
}
53+
const slug = repository.slice('github:'.length)
54+
const parts = slug.split('/')
55+
if (
56+
parts.length !== 2 ||
57+
!parts[0] ||
58+
!parts[1] ||
59+
parts.some(part => !/^[A-Za-z0-9_.-]+$/u.test(part))
60+
) {
61+
throw new Error(
62+
'external-tools.json repository is not a github:owner/repo reference',
63+
)
64+
}
65+
return slug
66+
}
67+
68+
export interface ReleaseAssetTool {
69+
readonly origin?: unknown | undefined
70+
readonly repository?: unknown | undefined
71+
readonly tag?: unknown | undefined
72+
readonly version?: unknown | undefined
73+
}
74+
75+
export interface ReleaseAssetEntry {
76+
readonly asset?: unknown | undefined
77+
readonly integrity?: unknown | undefined
78+
}
79+
80+
export interface ResolvedCatalogAsset {
81+
readonly asset: string
82+
readonly assetName?: string | undefined
83+
readonly integrity: string
84+
readonly repository?: string | undefined
85+
readonly src: string
86+
readonly date: string
87+
readonly tag?: string | undefined
88+
readonly version: string
89+
}
90+
91+
/**
92+
* Resolve a pinned GitHub release asset and verify its URL binding.
93+
*/
94+
export function resolveGithubReleaseAsset(
95+
tool: ReleaseAssetTool,
96+
entry: ReleaseAssetEntry,
97+
canonicalKey: string,
98+
): ResolvedCatalogAsset {
99+
const slug = githubRepositorySlug(tool.repository)
100+
const tag = safeReleaseSegment(tool.tag, 'tag')
101+
const assetName = safeReleaseSegment(entry.asset, 'platform asset')
102+
const pathname = `/${slug}/releases/download/${encodeURIComponent(tag)}/${encodeURIComponent(assetName)}`
103+
const asset = new URL(pathname, GITHUB_ORIGIN)
104+
if (
105+
asset.origin !== GITHUB_ORIGIN ||
106+
asset.pathname !== pathname ||
107+
asset.username ||
108+
asset.password ||
109+
asset.search ||
110+
asset.hash
111+
) {
112+
throw new Error(
113+
`external-tools.json ${canonicalKey} release asset URL failed GitHub binding validation`,
114+
)
115+
}
116+
const integrity = integrityValue(entry.integrity)
117+
if (!integrity) {
118+
throw new Error(
119+
`external-tools.json ${canonicalKey} entry is missing integrity`,
120+
)
121+
}
122+
const { src, date } = integrityProvenance(entry.integrity)
123+
return {
124+
__proto__: null,
125+
asset: asset.href,
126+
assetName,
127+
integrity,
128+
repository: slug,
129+
src,
130+
date,
131+
tag,
132+
version: String(tool.version ?? ''),
133+
} as ResolvedCatalogAsset
134+
}
135+
136+
/**
137+
* Resolve a catalog asset while preserving its exact integrity metadata.
138+
*/
139+
export function resolveCatalogAsset(
140+
tool: ReleaseAssetTool,
141+
entry: ReleaseAssetEntry,
142+
canonicalKey: string,
143+
): ResolvedCatalogAsset {
144+
const isGithub =
145+
tool.origin === 'gh-asset' ||
146+
(typeof tool.repository === 'string' &&
147+
tool.repository.startsWith('github:'))
148+
if (isGithub) {
149+
return resolveGithubReleaseAsset(tool, entry, canonicalKey)
150+
}
151+
const asset = entry.asset
152+
const integrity = integrityValue(entry.integrity)
153+
if (typeof asset !== 'string' || !asset.startsWith('https://')) {
154+
throw new Error(
155+
`external-tools.json ${canonicalKey} entry is missing an HTTPS asset URL`,
156+
)
157+
}
158+
if (!integrity) {
159+
throw new Error(
160+
`external-tools.json ${canonicalKey} entry is missing integrity`,
161+
)
162+
}
163+
const { src, date } = integrityProvenance(entry.integrity)
164+
return {
165+
__proto__: null,
166+
asset,
167+
integrity,
168+
src,
169+
date,
170+
version: String(tool.version ?? ''),
171+
} as ResolvedCatalogAsset
172+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/** Type declarations for the generated dependency-free release asset resolver. */
2+
3+
export interface ReleaseAssetEntry {
4+
readonly asset?: unknown
5+
readonly integrity?: unknown
6+
}
7+
8+
export interface ReleaseAssetTool {
9+
readonly origin?: unknown
10+
readonly repository?: unknown
11+
readonly tag?: unknown
12+
readonly version?: unknown
13+
}
14+
15+
export interface PlatformEntry extends ReleaseAssetEntry {
16+
readonly asset: string
17+
}
18+
19+
export interface ResolvedCatalogAsset {
20+
readonly asset: string
21+
readonly assetName?: string
22+
readonly integrity: string
23+
readonly repository?: string
24+
readonly src: string
25+
readonly date: string
26+
readonly tag?: string
27+
readonly version: string
28+
}
29+
30+
export const GO_OS_ARCH: Readonly<Record<string, { readonly os: string; readonly arch: string }>>
31+
export function canonicalPlatformKey(): string
32+
export function integrityProvenance(integrity: unknown): { readonly src: string; readonly date: string }
33+
export function integrityValue(integrity: unknown): string
34+
export function readVersionFromFile(file: string): string
35+
export function resolveCatalogAsset(
36+
tool: ReleaseAssetTool,
37+
entry: ReleaseAssetEntry,
38+
canonicalKey: string,
39+
): ResolvedCatalogAsset
40+
export function resolveGithubReleaseAsset(
41+
tool: ReleaseAssetTool,
42+
entry: ReleaseAssetEntry,
43+
canonicalKey: string,
44+
): ResolvedCatalogAsset
45+
export function resolveGoAssetFromManifest(
46+
manifest: unknown,
47+
version: string,
48+
canonicalKey: string,
49+
): { readonly asset: string; readonly integrity: string; readonly version: string }
50+
export function resolvePlatformEntry(
51+
platforms: Readonly<Record<string, PlatformEntry>>,
52+
canonicalKey: string,
53+
): {
54+
readonly entry: PlatformEntry | undefined
55+
readonly fallbackKey: string | undefined
56+
}

0 commit comments

Comments
 (0)