README.md
Rendering markdown...
#!/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");
}