Skip to content

Commit 3b96703

Browse files
committed
feat: add signature to ZIP tarball URL stored in KV store
1 parent c29140c commit 3b96703

File tree

4 files changed

+60
-8
lines changed

4 files changed

+60
-8
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"@apify/actor-templates": "^0.1.5",
6767
"@apify/consts": "^2.36.0",
6868
"@apify/input_schema": "^3.17.0",
69-
"@apify/utilities": "^2.15.1",
69+
"@apify/utilities": "^2.18.0",
7070
"@crawlee/memory-storage": "^3.12.0",
7171
"@inquirer/core": "^10.1.15",
7272
"@inquirer/input": "^4.2.1",
@@ -79,7 +79,7 @@
7979
"@skyra/jaro-winkler": "^1.1.1",
8080
"adm-zip": "~0.5.15",
8181
"ajv": "~8.17.1",
82-
"apify-client": "^2.12.6",
82+
"apify-client": "^2.13.0",
8383
"archiver": "~7.0.1",
8484
"axios": "^1.11.0",
8585
"chalk": "~5.5.0",

src/commands/actors/push.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import open from 'open';
88

99
import { fetchManifest } from '@apify/actor-templates';
1010
import { ACTOR_JOB_STATUSES, ACTOR_SOURCE_TYPES, MAX_MULTIFILE_BYTES } from '@apify/consts';
11+
import { createHmacSignature } from '@apify/utilities';
1112

1213
import { ApifyCommand } from '../../lib/command-framework/apify-command.js';
1314
import { Args } from '../../lib/command-framework/args.js';
@@ -258,7 +259,24 @@ Skipping push. Use --force to override.`,
258259
contentType: 'application/zip',
259260
});
260261
unlinkSync(TEMP_ZIP_FILE_NAME);
261-
tarballUrl = `${apifyClient.baseUrl}/key-value-stores/${store.id}/records/${key}?disableRedirect=true`;
262+
const tempTarballUrl = new URL(
263+
`${apifyClient.baseUrl}/key-value-stores/${store.id}/records/${key}?disableRedirect=true`,
264+
);
265+
266+
/**
267+
* Signs the tarball URL to grant temporary access for restricted resources.
268+
* When a store is set to 'RESTRICTED', direct URLs are disabled. Instead of
269+
* appending a security token, we add a signature to the URL parameters.
270+
* https://github.com/apify/apify-core/issues/22197
271+
*
272+
* TODO: Use keyValueStore(:storeId).getRecordPublicUrl from apify-client instead once it is released.
273+
*/
274+
if (store?.urlSigningSecretKey) {
275+
const signature = createHmacSignature(store.urlSigningSecretKey, key);
276+
tempTarballUrl.searchParams.set('signature', signature);
277+
}
278+
279+
tarballUrl = tempTarballUrl.toString();
262280
sourceType = ACTOR_SOURCE_TYPES.TARBALL;
263281
}
264282

test/api/commands/push.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { mkdir, writeFile } from 'node:fs/promises';
44
import type { ActorCollectionCreateOptions } from 'apify-client';
55

66
import { ACTOR_SOURCE_TYPES, SOURCE_FILE_FORMATS } from '@apify/consts';
7+
import { createHmacSignature } from '@apify/utilities';
78

89
import { testRunCommand } from '../../../src/lib/command-framework/apify-command.js';
910
import { LOCAL_CONFIG_PATH } from '../../../src/lib/consts.js';
@@ -236,6 +237,10 @@ describe('[api] apify push', () => {
236237
testActor = (await testActorClient.get())!;
237238
const testActorVersion = await testActorClient.version(actorJson.version).get();
238239
const store = await testUserClient.keyValueStores().getOrCreate(`actor-${testActor.id}-source`);
240+
241+
expect(store.urlSigningSecretKey).toBeDefined();
242+
const signature = createHmacSignature(store.urlSigningSecretKey!, `version-${actorJson.version}.zip`);
243+
239244
await testUserClient.keyValueStore(store.id).delete(); // We just needed the store ID, we can clean up now
240245

241246
if (testActor) await testActorClient.delete();
@@ -245,7 +250,7 @@ describe('[api] apify push', () => {
245250
buildTag: 'latest',
246251
tarballUrl:
247252
`${testActorClient.baseUrl}/key-value-stores/${store.id}` +
248-
`/records/version-${actorJson.version}.zip?disableRedirect=true`,
253+
`/records/version-${actorJson.version}.zip?disableRedirect=true&signature=${signature}`,
249254
envVars: testActorWithEnvVars.versions[0].envVars,
250255
sourceType: ACTOR_SOURCE_TYPES.TARBALL,
251256
});

yarn.lock

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ __metadata:
117117
languageName: node
118118
linkType: hard
119119

120-
"@apify/utilities@npm:^2.13.0, @apify/utilities@npm:^2.15.1, @apify/utilities@npm:^2.17.0, @apify/utilities@npm:^2.7.10":
120+
"@apify/utilities@npm:^2.13.0, @apify/utilities@npm:^2.17.0, @apify/utilities@npm:^2.7.10":
121121
version: 2.17.0
122122
resolution: "@apify/utilities@npm:2.17.0"
123123
dependencies:
@@ -127,6 +127,16 @@ __metadata:
127127
languageName: node
128128
linkType: hard
129129

130+
"@apify/utilities@npm:^2.18.0":
131+
version: 2.18.0
132+
resolution: "@apify/utilities@npm:2.18.0"
133+
dependencies:
134+
"@apify/consts": "npm:^2.43.0"
135+
"@apify/log": "npm:^2.5.20"
136+
checksum: 10c0/1b65891901c6bf6725d00b4c7e00104e2b7e3ecf02ebc86bf816459308028cbd3e8415ffea7ccf562a719f53c15a8118d1d534511e9b940f767d46a1abf2c792
137+
languageName: node
138+
linkType: hard
139+
130140
"@arcanis/slice-ansi@npm:^1.1.1":
131141
version: 1.1.1
132142
resolution: "@arcanis/slice-ansi@npm:1.1.1"
@@ -2244,7 +2254,7 @@ __metadata:
22442254
"@apify/eslint-config": "npm:^1.0.0"
22452255
"@apify/input_schema": "npm:^3.17.0"
22462256
"@apify/tsconfig": "npm:^0.1.1"
2247-
"@apify/utilities": "npm:^2.15.1"
2257+
"@apify/utilities": "npm:^2.18.0"
22482258
"@biomejs/biome": "npm:^2.0.0"
22492259
"@crawlee/memory-storage": "npm:^3.12.0"
22502260
"@crawlee/types": "npm:^3.11.1"
@@ -2278,7 +2288,7 @@ __metadata:
22782288
adm-zip: "npm:~0.5.15"
22792289
ajv: "npm:~8.17.1"
22802290
apify: "npm:^3.2.4"
2281-
apify-client: "npm:^2.12.6"
2291+
apify-client: "npm:^2.13.0"
22822292
archiver: "npm:~7.0.1"
22832293
axios: "npm:^1.11.0"
22842294
chai: "npm:^4.4.1"
@@ -2330,7 +2340,7 @@ __metadata:
23302340
languageName: unknown
23312341
linkType: soft
23322342

2333-
"apify-client@npm:^2.12.1, apify-client@npm:^2.12.6":
2343+
"apify-client@npm:^2.12.1":
23342344
version: 2.12.6
23352345
resolution: "apify-client@npm:2.12.6"
23362346
dependencies:
@@ -2348,6 +2358,25 @@ __metadata:
23482358
languageName: node
23492359
linkType: hard
23502360

2361+
"apify-client@npm:^2.13.0":
2362+
version: 2.13.0
2363+
resolution: "apify-client@npm:2.13.0"
2364+
dependencies:
2365+
"@apify/consts": "npm:^2.25.0"
2366+
"@apify/log": "npm:^2.2.6"
2367+
"@apify/utilities": "npm:^2.18.0"
2368+
"@crawlee/types": "npm:^3.3.0"
2369+
agentkeepalive: "npm:^4.2.1"
2370+
async-retry: "npm:^1.3.3"
2371+
axios: "npm:^1.6.7"
2372+
content-type: "npm:^1.0.5"
2373+
ow: "npm:^0.28.2"
2374+
tslib: "npm:^2.5.0"
2375+
type-fest: "npm:^4.0.0"
2376+
checksum: 10c0/0596ba6aa4a534fc514e4d32b5ed232f1be7fd49cd3759380332edd136f5697add34a9e2ff3f8e98088f9945faf64ea5a7ebe53eb3d7b3886333ab3eddddfa32
2377+
languageName: node
2378+
linkType: hard
2379+
23512380
"apify@npm:^3.2.4":
23522381
version: 3.4.4
23532382
resolution: "apify@npm:3.4.4"

0 commit comments

Comments
 (0)