Skip to content

Commit f0f322e

Browse files
committed
feat: export install-source helpers
1 parent e9c4730 commit f0f322e

5 files changed

Lines changed: 33 additions & 4 deletions

File tree

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
"import": "./dist/src/remote-config.js",
2121
"types": "./dist/src/remote-config.d.ts"
2222
},
23+
"./install-source": {
24+
"import": "./dist/src/install-source.js",
25+
"types": "./dist/src/install-source.d.ts"
26+
},
2327
"./contracts": {
2428
"import": "./dist/src/contracts.js",
2529
"types": "./dist/src/contracts.d.ts"

rslib.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default defineConfig({
1717
source: {
1818
entry: {
1919
index: 'src/index.ts',
20+
'install-source': 'src/install-source.ts',
2021
metro: 'src/metro.ts',
2122
'remote-config': 'src/remote-config.ts',
2223
contracts: 'src/contracts.ts',

src/install-source.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export {
2+
ARCHIVE_EXTENSIONS,
3+
isBlockedIpAddress,
4+
isBlockedSourceHostname,
5+
isTrustedInstallSourceUrl,
6+
materializeInstallablePath,
7+
validateDownloadSourceUrl,
8+
} from './platforms/install-source.ts';
9+
10+
export type {
11+
MaterializeInstallSource,
12+
MaterializedInstallable,
13+
} from './platforms/install-source.ts';

src/platforms/__tests__/install-source.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import fs from 'node:fs/promises';
66
import os from 'node:os';
77
import path from 'node:path';
88
import {
9+
ARCHIVE_EXTENSIONS,
10+
isBlockedIpAddress,
11+
isBlockedSourceHostname,
912
isTrustedInstallSourceUrl,
1013
materializeInstallablePath,
1114
validateDownloadSourceUrl,
12-
} from '../install-source.ts';
15+
} from '../../install-source.ts';
1316
import { prepareAndroidInstallArtifact } from '../android/install-artifact.ts';
1417
import { prepareIosInstallArtifact } from '../ios/install-artifact.ts';
1518

@@ -46,6 +49,14 @@ test('validateDownloadSourceUrl rejects unsupported protocols', async () => {
4649
);
4750
});
4851

52+
test('public install-source helpers expose the SSRF and archive surface', () => {
53+
assert.deepEqual(ARCHIVE_EXTENSIONS, ['.zip', '.tar', '.tar.gz', '.tgz']);
54+
assert.equal(isBlockedSourceHostname('localhost'), true);
55+
assert.equal(isBlockedSourceHostname('example.com'), false);
56+
assert.equal(isBlockedIpAddress('127.0.0.1'), true);
57+
assert.equal(isBlockedIpAddress('203.0.113.10'), false);
58+
});
59+
4960
test('isTrustedInstallSourceUrl recognizes supported artifact services', () => {
5061
assert.equal(
5162
isTrustedInstallSourceUrl('https://api.github.com/repos/acme/app/actions/artifacts/1/zip'),

src/platforms/install-source.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export type MaterializedInstallable = {
4242
cleanup: () => Promise<void>;
4343
};
4444

45-
const ARCHIVE_EXTENSIONS = ['.zip', '.tar', '.tar.gz', '.tgz'] as const;
45+
export const ARCHIVE_EXTENSIONS = ['.zip', '.tar', '.tar.gz', '.tgz'] as const;
4646
const MAX_INSTALL_SOURCE_SEARCH_DEPTH = 5;
4747
const DEFAULT_SOURCE_DOWNLOAD_TIMEOUT_MS = resolveTimeoutMs(
4848
process.env.AGENT_DEVICE_SOURCE_DOWNLOAD_TIMEOUT_MS,
@@ -271,13 +271,13 @@ function resolveDownloadFileName(response: Response, parsedUrl: URL): string {
271271
return 'downloaded-artifact.bin';
272272
}
273273

274-
function isBlockedSourceHostname(hostname: string): boolean {
274+
export function isBlockedSourceHostname(hostname: string): boolean {
275275
if (!hostname) return true;
276276
if (hostname === 'localhost' || hostname.endsWith('.localhost')) return true;
277277
return isBlockedIpAddress(hostname);
278278
}
279279

280-
function isBlockedIpAddress(address: string): boolean {
280+
export function isBlockedIpAddress(address: string): boolean {
281281
const family = net.isIP(address);
282282
if (family === 4) return isBlockedIpv4(address);
283283
if (family === 6) return isBlockedIpv6(address);

0 commit comments

Comments
 (0)