From 73acd13c02f490a74304cf9dd813299100d47691 Mon Sep 17 00:00:00 2001 From: ytkimirti Date: Mon, 28 Sep 2026 08:47:58 +0300 Subject: [PATCH] Upgrade @upstash/blob to 0.0.7, cap blob presign at 10 minutes --- README.md | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- src/commands/blob/presign.ts | 13 +++++-------- tests/unit/blob-s3.test.ts | 2 ++ 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d6d029f..b437b34 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ upstash blob cp blob://my-bucket/config.json - | jq . upstash blob mv blob://my-bucket/a.txt blob://other-bucket/a.txt upstash blob sync ./site blob://my-bucket/site --delete upstash blob rm blob://my-bucket/tmp --recursive --dryrun -upstash blob presign blob://my-bucket/report.pdf --expires-in 3600 +upstash blob presign blob://my-bucket/report.pdf --expires-in 600 upstash blob mb blob://new-bucket upstash blob rb blob://new-bucket --force ``` diff --git a/package-lock.json b/package-lock.json index 86ceb97..3938150 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "license": "MIT", "dependencies": { - "@upstash/blob": "0.0.5", + "@upstash/blob": "0.0.7", "commander": "^13.0.0", "dotenv": "^16.4.5", "mime": "4.1.0" @@ -792,9 +792,9 @@ } }, "node_modules/@upstash/blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@upstash/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-5go9FxMd0yJU3g09+UyKDxGCGkwKJ9YG7kFRHpwEvTtHAuWuQtu4nkjuunRh6fuNiFMplVNLvTjosSD5UT/ZVA==", + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@upstash/blob/-/blob-0.0.7.tgz", + "integrity": "sha512-Q2+GqZdnZxjzdHrROd/dSGVmR7im4FJ6SSKh5qfOLyYiZmtzMiLjd+yG0vwq2urtZ+UswpF4MlLQ//UEIUNkCw==", "license": "MIT", "engines": { "node": ">=20" diff --git a/package.json b/package.json index 2830931..f3084dd 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "author": "Upstash", "license": "MIT", "dependencies": { - "@upstash/blob": "0.0.5", + "@upstash/blob": "0.0.7", "commander": "^13.0.0", "dotenv": "^16.4.5", "mime": "4.1.0" diff --git a/src/commands/blob/presign.ts b/src/commands/blob/presign.ts index f6bd813..d65d74f 100644 --- a/src/commands/blob/presign.ts +++ b/src/commands/blob/presign.ts @@ -3,7 +3,8 @@ import { printJSON } from "../../output.js"; import { BucketResolver } from "./buckets.js"; import { formatLocation, parseBlobLocation } from "./transfer.js"; -const MAX_EXPIRES_IN = 7 * 24 * 60 * 60; +// Upstash signs the URL and caps it at 10 minutes. +const MAX_EXPIRES_IN = 600; function expiresIn(value: string): number { const seconds = Number(value); @@ -17,21 +18,17 @@ export function registerBlobPresign(blob: Command): void { blob .command("presign ") .description("Create a temporary download URL for an object, like aws s3 presign") - .option("--expires-in ", "How long the URL should work", expiresIn, 3600) + .option("--expires-in ", `How long the URL should work, at most ${MAX_EXPIRES_IN}`, expiresIn, MAX_EXPIRES_IN) .option("--token ", "Blob bucket token, used for the bucket it was issued for (default: UPSTASH_BLOB_TOKEN)") .addHelpText("after", ` -A URL never outlives the temporary credential that signs it, so it can expire -sooner than requested; expires_at is when it actually stops working. +Upstash signs the URL for this one object; it carries no bucket credential and +lives at most 10 minutes. expires_at is when it actually stops working. `) .action(async (uri: string, options: { expiresIn: number; token?: string }, cmd: Command) => { const location = parseBlobLocation(uri); if (!location.key || location.key.endsWith("/")) throw new Error(`${formatLocation(location)} names no object`); const bucket = await new BucketResolver(cmd, options.token).open(location.bucket); const signed = await bucket.signedReadUrl(location.key, { expiresIn: options.expiresIn }); - const requested = Date.now() + options.expiresIn * 1000; - if (signed.expiresAt.getTime() < requested - 60_000) { - console.error(`note: the URL expires at ${signed.expiresAt.toISOString()}, sooner than requested, because its signing credential expires then`); - } printJSON({ url: signed.url, expires_at: signed.expiresAt.toISOString() }); }); } diff --git a/tests/unit/blob-s3.test.ts b/tests/unit/blob-s3.test.ts index 6b38178..0a0db26 100644 --- a/tests/unit/blob-s3.test.ts +++ b/tests/unit/blob-s3.test.ts @@ -250,6 +250,8 @@ describe("argument checks", () => { await expect(runCommand(await createBlobProgram(), ["blob", "rm", "blob://b"])).rejects.toThrow("names no object"); await expect(runCommand(await createBlobProgram(), ["blob", "presign", "blob://b/k", "--expires-in", "0"])) .rejects.toThrow(); + await expect(runCommand(await createBlobProgram(), ["blob", "presign", "blob://b/k", "--expires-in", "601"])) + .rejects.toThrow("from 1 to 600"); await expect(runCommand(await createBlobProgram(), ["blob", "mb", "blob://b/key"])).rejects.toThrow("includes a key"); }); });