#!/usr/bin/env node import { createPresignedPost, getKeyBuffer, initService } from "./presign.js"; await initService(); console.log("Example 0: Simple upload (should work)") { const { url, fields } = await createPresignedPost("buffer.txt", [["content-length-range", 0, 10000]]); const buffer = Buffer.from("this is a buffer"); console.log("This upload should succeed:") await postUpload(buffer, url, fields); console.log(); const [bufferOnServer] = await getKeyBuffer("buffer.txt"); console.log("Buffer on server:", bufferOnServer.toString("utf-8")); } console.log("---"); console.log("Example 1: Upload that is too large (should NOT work)") { const { url, fields } = await createPresignedPost("buffer.txt", [["content-length-range", 0, 4]]); const buffer = Buffer.from("this buffer is larger than 4 bytes"); console.log("This upload should fail:") await postUpload(buffer, url, fields); console.log(); const [bufferOnServer] = await getKeyBuffer("buffer.txt").catch(() => [null]); // the previous upload should have failed, so the content on the server should be unchanged console.log("Buffer on server:", bufferOnServer?.toString("utf-8")); } console.log("---"); console.log("Example 2: Upload has a key that is outside of the allowed prefix (should NOT work)"); { const { url, fields } = await createPresignedPost("dontcare.txt", [["starts-with", "$key", "user/uploads/"]]); const buffer = Buffer.from("some data from example 2"); fields["key"] = "secret/area/foo.txt"; // trying to upload outside of allowed prefix console.log("This upload should fail:") await postUpload(buffer, url, fields); console.log(); const [bufferOnServer] = await getKeyBuffer("secret/area/foo.txt").catch(() => [null]); // The object should not be written since the key is outside of the allowed prefix console.log("Buffer on server:", bufferOnServer?.toString("utf-8")); } console.log("---"); console.log("Example 3: Upload has a content-type that is not allowd (should NOT work)"); { const { url, fields } = await createPresignedPost("not-an-image/buffer.txt", [{"Content-Type": "image/png"}]); const buffer = Buffer.from("some data from example 3"); console.log("This upload should fail:") await postUpload(buffer, url, fields); // uses text/plain instead of image/png // an attacker would use text/html and possibly use this for XSS on a trusted domain console.log(); const [bufferOnServer, contentType] = await getKeyBuffer("not-an-image/buffer.txt").catch(() => [null, null]); // The object should not be written since it had the wrong content-type console.log("Buffer on server:", bufferOnServer?.toString("utf-8")); console.log("Persisted Content-Type on server:", contentType); // should not be "text/plain" } async function postUpload(buffer, url, fields) { const body = new FormData(); for (const [key, value] of Object.entries(fields)) { body.append(key, value); } body.append("file", new Blob([buffer], { type: "text/plain" })); const res = await fetch(url, { method: "POST", body, }); if (!res.ok) { const text = await res.text(); console.log(`Upload failed: ${res.status}\n${text}`); return; } console.log("Upload Successful"); }