mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
fix(s3): sandbox stored XSS via download response headers (#9263)
* [ee] fix(s3): sandbox stored XSS via download response headers Reported chain: a workspace user uploads xss.html via apps_u/upload_s3_file with content_type=text/html&content_disposition=inline; when an admin clicks the resulting download URL the browser renders the attacker page in Windmill's origin and can escalate via the SameSite=Lax session cookie. Fix on the download side only — leaves upload semantics unchanged so existing integrations are not affected: - download_s3_file_internal (used by apps_u/download_s3_file and job_helpers/download_s3_file) emits X-Content-Type-Options: nosniff and Content-Security-Policy: sandbox on every response (EE). - The HTTP static-asset trigger emits the same headers on single-file responses. Static-website responses keep their existing semantics (CSP sandbox would break a legitimate static site); restricting write access to those buckets remains the documented mitigation. Sandbox loads any HTML/SVG into an opaque origin so the page cannot reach the viewer's cookie or /api/*. Images, PDFs, and fetch-driven previews are unaffected (browsers ignore CSP for <img>/<embed> and for fetch responses). Companion: windmill-ee-private fix/s3-content-type-xss. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to daffe7bb81cfcaca666c61de1ee838a44d60ebc2 This commit updates the EE repository reference after PR #585 was merged in windmill-ee-private. Previous ee-repo-ref: e889b86ee1c68c2f7cf9b07ec4b8ba6e6b66a169 New ee-repo-ref: daffe7bb81cfcaca666c61de1ee838a44d60ebc2 Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
3489c243b0e5a8eb0dbc86e90917fbe72843573b
|
||||
daffe7bb81cfcaca666c61de1ee838a44d60ebc2
|
||||
|
||||
@@ -424,6 +424,7 @@ async fn route_job(
|
||||
.flatten()
|
||||
.unwrap_or("application/octet-stream".parse().unwrap()),
|
||||
);
|
||||
response_headers.insert("x-content-type-options", "nosniff".parse().unwrap());
|
||||
if !trigger.is_static_website {
|
||||
response_headers.insert(
|
||||
"content-disposition",
|
||||
@@ -443,6 +444,19 @@ async fn route_job(
|
||||
},
|
||||
),
|
||||
);
|
||||
// For single-file triggers, sandbox any HTML/SVG so it can't
|
||||
// reach the viewer's session cookie. Allow-scripts/forms/etc.
|
||||
// keep the opaque origin (cookies still blocked) while
|
||||
// preserving JS for legitimate HTML payloads. Static-website
|
||||
// triggers intentionally serve a live web app and cannot be
|
||||
// sandboxed; restrict write access to those buckets at the
|
||||
// workspace level.
|
||||
response_headers.insert(
|
||||
"content-security-policy",
|
||||
"sandbox allow-scripts allow-forms allow-popups allow-modals allow-downloads"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
let body_stream = axum::body::Body::from_stream(s3_object.into_stream());
|
||||
|
||||
Reference in New Issue
Block a user