Skip to content

Adopts #10490: feat: support v4 signed url endpoint#10709

Open
annajowang wants to merge 9 commits into
mainfrom
test-pr-10490
Open

Adopts #10490: feat: support v4 signed url endpoint#10709
annajowang wants to merge 9 commits into
mainfrom
test-pr-10490

Conversation

@annajowang

@annajowang annajowang commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

adopts PR# 10490 for merging

@wiz-9635d3485b

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 2 Medium
Software Management Finding Software Management Findings -
Total 2 Medium

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

@annajowang annajowang requested review from Yuangwang and falahat June 24, 2026 16:25

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for signed POST policy uploads via the emulator endpoint by introducing a new multipart/form-data parser and integrating it into the GCS emulator. Feedback on the changes highlights an invalid TypeScript type assertion when mapping headers to metadata, suggesting a more type-safe approach, and points out that the content-type check and boundary extraction should be case-insensitive to comply with RFC 2045.

Comment on lines +400 to +415
const HEADER_MAP: Record<string, keyof IncomingMetadata> = {
"content-type": "contentType",
"cache-control": "cacheControl",
"content-disposition": "contentDisposition",
"content-encoding": "contentEncoding",
"content-language": "contentLanguage",
};
for (const part of formData) {
if (part.type === "file" || part.name === "key") continue;
const key = part.name.toLowerCase();
if (key.startsWith("x-goog-meta-")) {
metadata.metadata![key.substring(12)] = part.value;
} else if (HEADER_MAP[key]) {
(metadata[HEADER_MAP[key]] as string) = part.value.trim();
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The type assertion on the left-hand side of the assignment (metadata[HEADER_MAP[key]] as string) = ... is invalid TypeScript and can cause compilation errors depending on the compiler configuration. We can make this completely type-safe and avoid any type assertions by typing HEADER_MAP more precisely using Omit to exclude non-string properties of IncomingMetadata.

        const HEADER_MAP: Record<string, keyof Omit<IncomingMetadata, "metadata" | "name">> = {
          "content-type": "contentType",
          "cache-control": "cacheControl",
          "content-disposition": "contentDisposition",
          "content-encoding": "contentEncoding",
          "content-language": "contentLanguage",
        };
        for (const part of formData) {
          if (part.type === "file" || part.name === "key") continue;
          const key = part.name.toLowerCase();
          if (key.startsWith("x-goog-meta-")) {
            metadata.metadata![key.substring(12)] = part.value;
          } else {
            const metaKey = HEADER_MAP[key];
            if (metaKey) {
              metadata[metaKey] = part.value.trim();
            }
          }
        }

Comment on lines +268 to +274
if (!contentTypeHeader.startsWith("multipart/form-data")) {
throw new Error(`Bad content type. ${contentTypeHeader}`);
}
const boundaryId = contentTypeHeader.split("boundary=")[1]?.split(";")[0]?.trim();
if (!boundaryId) {
throw new Error(`Bad content type. ${contentTypeHeader}`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The contentTypeHeader check and boundary extraction are case-sensitive, which violates RFC 2045 (stating that media types and parameter names are case-insensitive). For example, if a client sends Multipart/Form-Data or uses Boundary=..., the parsing will fail. We should perform a case-insensitive check on the media type and use a regular expression to extract the boundary parameter case-insensitively while preserving the case of the boundary value itself.

Suggested change
if (!contentTypeHeader.startsWith("multipart/form-data")) {
throw new Error(`Bad content type. ${contentTypeHeader}`);
}
const boundaryId = contentTypeHeader.split("boundary=")[1]?.split(";")[0]?.trim();
if (!boundaryId) {
throw new Error(`Bad content type. ${contentTypeHeader}`);
}
if (!contentTypeHeader.toLowerCase().startsWith("multipart/form-data")) {
throw new Error("Bad content type. " + contentTypeHeader);
}
const boundaryMatch = contentTypeHeader.match(/boundary\s*=\s*([^;]+)/i);
const boundaryId = boundaryMatch?.[1]?.trim();
if (!boundaryId) {
throw new Error("Bad content type. " + contentTypeHeader);
}

@annajowang annajowang removed the request for review from Yuangwang June 24, 2026 16:26
@annajowang annajowang changed the title Mirror #10490 for CI: feat: support v4 signed url endpoint Adopts #10490: feat: support v4 signed url endpoint Jun 24, 2026
return res.sendStatus(404);
}
if (err instanceof Error) {
return res.status(400).send(err.message);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants