fix(security): enforce ownership on private draft blob deletion

This commit is contained in:
2026-11-05 13:04:09 +00:00
parent 056dace563
commit fc960f907f
3 changed files with 510 additions and 23 deletions
@@ -1,6 +1,8 @@
import { hashAPIPath } from "../../../actions/core/hash";
import { deleteBlob } from "../../../actions/azurestorage";
import { respondError, respondSuccess } from "../middleware/apiResponse";
import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
import nextConnect from "next-connect";
import middleware from "../middleware/middleware";
@@ -8,6 +10,22 @@ import middleware from "../middleware/middleware";
const ApiProxy = nextConnect();
ApiProxy.use(middleware);
const forbidden = (res) =>
respondError(res, {
status: 403,
code: "FORBIDDEN",
message: "Request not permitted"
});
const hasValue = (value) =>
typeof value === "string" && value.trim().length > 0;
const isValidContainer = (value) =>
typeof value === "string" &&
value.length >= 3 &&
value.length <= 63 &&
/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value);
ApiProxy.get(async (req, res) => {
const containerName = req.query.container;
const casefolderID = req.query.casefolderID;
@@ -15,14 +33,10 @@ ApiProxy.get(async (req, res) => {
const checkHash = req.query.hash;
if (
typeof containerName === "undefined" ||
containerName.length === 0 ||
typeof casefolderID === "undefined" ||
casefolderID.length === 0 ||
typeof blobName === "undefined" ||
blobName.length === 0 ||
typeof checkHash === "undefined" ||
checkHash.length === 0
!hasValue(containerName) ||
!hasValue(casefolderID) ||
!hasValue(blobName) ||
!hasValue(checkHash)
) {
return respondError(res, {
status: 400,
@@ -34,6 +48,23 @@ ApiProxy.get(async (req, res) => {
const casefolderIDTrimmed = casefolderID.trim();
const blobNameTrimmed = blobName.trim();
if (!isValidContainer(containerName)) {
return respondError(res, {
status: 400,
code: "INVALID_REQUEST",
message: "Invalid request"
});
}
const session = await getServerSession(req, res, authOptions(req, res));
if (
typeof session?.user?.id !== "string" ||
session.user.id.length === 0 ||
session.user.id !== containerName
) {
return forbidden(res);
}
const hashCandidatePaths = [
"/api/file/deleteawaitingsubmissionfromblob?container=" +
containerName +
+39 -8
View File
@@ -1,6 +1,8 @@
import { hashAPIPath } from "../../../actions/core/hash";
import { deleteBlob } from "../../../actions/azurestorage";
import { respondError, respondSuccess } from "../middleware/apiResponse";
import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
import nextConnect from "next-connect";
import middleware from "../middleware/middleware";
@@ -8,6 +10,22 @@ import middleware from "../middleware/middleware";
const ApiProxy = nextConnect();
ApiProxy.use(middleware);
const forbidden = (res) =>
respondError(res, {
status: 403,
code: "FORBIDDEN",
message: "Request not permitted"
});
const hasValue = (value) =>
typeof value === "string" && value.trim().length > 0;
const isValidContainer = (value) =>
typeof value === "string" &&
value.length >= 3 &&
value.length <= 63 &&
/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value);
ApiProxy.get(async (req, res) => {
const containerName = req.query.container;
const casefolderID = req.query.casefolderID;
@@ -15,14 +33,10 @@ ApiProxy.get(async (req, res) => {
const checkHash = req.query.hash;
if (
typeof containerName === "undefined" ||
containerName.length === 0 ||
typeof casefolderID === "undefined" ||
casefolderID.length === 0 ||
typeof blobName === "undefined" ||
blobName.length === 0 ||
typeof checkHash === "undefined" ||
checkHash.length === 0
!hasValue(containerName) ||
!hasValue(casefolderID) ||
!hasValue(blobName) ||
!hasValue(checkHash)
) {
return respondError(res, {
status: 400,
@@ -34,6 +48,23 @@ ApiProxy.get(async (req, res) => {
const casefolderIDTrimmed = casefolderID.trim();
const blobNameTrimmed = blobName.trim();
if (!isValidContainer(containerName)) {
return respondError(res, {
status: 400,
code: "INVALID_REQUEST",
message: "Invalid request"
});
}
const session = await getServerSession(req, res, authOptions(req, res));
if (
typeof session?.user?.id !== "string" ||
session.user.id.length === 0 ||
session.user.id !== containerName
) {
return forbidden(res);
}
const hashCandidatePaths = [
"/api/file/deleteblob?container=" +
containerName +