Skip to content

Validate response status and MIME type in WorkerGlobalScope::ImportScripts#40023

Merged
jdm merged 2 commits intoservo:mainfrom
WaterWhisperer:workerglobalscope-importscripts
Oct 21, 2025
Merged

Validate response status and MIME type in WorkerGlobalScope::ImportScripts#40023
jdm merged 2 commits intoservo:mainfrom
WaterWhisperer:workerglobalscope-importscripts

Conversation

@WaterWhisperer
Copy link
Copy Markdown
Contributor

@WaterWhisperer WaterWhisperer commented Oct 20, 2025

Changes

  • Add response status validation (must be an ok status)
  • Add MIME type validation (must be a JavaScript MIME type)
  • Both checks return NetworkError on failure, as required by the spec

Fixes: #39993

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Oct 20, 2025
@WaterWhisperer WaterWhisperer changed the title Validate response status and MIME type in WorkerGlobalScope::ImportScripts Validate response status and MIME type in WorkerGlobalScope::ImportScripts Oct 20, 2025
@WaterWhisperer WaterWhisperer force-pushed the workerglobalscope-importscripts branch from 3b6558e to 54b02cc Compare October 20, 2025 12:00
@TimvdLippe TimvdLippe added the T-linux-wpt Do a try run of the WPT label Oct 20, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Oct 20, 2025
@github-actions
Copy link
Copy Markdown

🔨 Triggering try run (#18652601184) for Linux (WPT)

@github-actions
Copy link
Copy Markdown

Test results for linux-wpt from try job (#18652601184):

Flaky unexpected result (19)
  • OK /IndexedDB/idbfactory_open.any.html
    • FAIL [expected PASS] subtest: Calling open() with version argument 1.5 should not throw.

      assert_equals: version expected 1 but got 9007199254740991
      

  • TIMEOUT [expected OK] /credential-management/credentialscontainer-frame-basics.https.html (#39430)
    • TIMEOUT [expected FAIL] subtest: navigator.credentials should be undefined in documents generated from data: URLs.

      Test timed out
      

  • OK /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • FAIL [expected PASS] subtest: Insert layer invalidates @font-face

      assert_equals: expected "220px" but got "133px"
      

    • FAIL [expected PASS] subtest: Delete layer invalidates @font-face

      assert_equals: expected "220px" but got "133px"
      

  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - name is missing
  • OK /fetch/content-length/api-and-duplicate-headers.any.html (#35873)
    • FAIL [expected PASS] subtest: fetch() and duplicate Content-Length/Content-Type headers

      promise_test: Unhandled rejection with value: object "TypeError: Network error occurred"
      

  • ERROR /fetch/metadata/generated/serviceworker.https.sub.html (#36247)
    • FAIL [expected PASS] subtest: sec-fetch-site - Same origin, no options - registration

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • TIMEOUT /html/browsers/history/the-history-interface/001.html (#12580)
    • FAIL [expected PASS] subtest: traversing history must also traverse hash changes

      assert_equals: (this could cause other failures later on) expected "" but got "test"
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_5.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html (#39694)
    • PASS [expected FAIL] subtest: Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due
  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html (#39702)
    • TIMEOUT [expected FAIL] subtest: Sandboxed iframe can not navigate other frame's popup

      Test timed out
      

  • OK /html/semantics/scripting-1/the-script-element/execution-timing/077.html (#22139)
    • FAIL [expected PASS] subtest: adding several types of scripts through the DOM and removing some of them confuses scheduler

      assert_array_equals: expected property 1 to be "Script #1 ran" but got "Script #3 ran" (expected array ["Script #2 ran", "Script #1 ran", "Script #3 ran", "Script #4 ran"] got ["Script #2 ran", "Script #3 ran", "Script #4 ran", "Script #1 ran"])
      

  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • PASS [expected FAIL] subtest: print() during unload
  • OK /preload/prefetch-document.html (#37210)
    • FAIL [expected PASS] subtest: different-site document prefetch with 'as=document' should not be consumed

      assert_equals: expected 2 but got 1
      

  • OK /preload/preload-xhr.html (#39092)
    • FAIL [expected PASS] subtest: Make an XHR request immediately after creating link rel=preload.

      assert_equals: resources/dummy.xml?token=2deff07b-9906-4d63-884e-2d700cb9274a expected 1 but got 0
      

  • TIMEOUT /resource-timing/test_resource_timing.html (#25720)
    • PASS [expected FAIL] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)
  • OK /webdriver/tests/classic/dismiss_alert/dismiss.py (#39098)
    • FAIL [expected PASS] subtest: test_dismiss_in_popup_window

      AssertionError: no such alert (404): No user prompt is currently active.
      

  • TIMEOUT [expected OK] /webdriver/tests/classic/perform_actions/key_events.py
  • CRASH [expected OK] /websockets/unload-a-document/001.html?wss
  • ERROR [expected OK] /workers/baseurl/alpha/import-in-moduleworker.html (#21315)
Stable unexpected results that are known to be intermittent (24)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in <script> tags.

      Test timed out
      

  • OK /IndexedDB/idbcursor-continuePrimaryKey-exceptions.any.worker.html (#39277)
    • FAIL [expected PASS] subtest: IDBCursor continuePrimaryKey() on object store cursor

      assert_throws_dom: continuePrimaryKey() should throw if source is not an index function "function() {
              cursor.continuePrimaryKey(2, 2);
            }" threw object "TypeError: cursor.continuePrimaryKey is not a function" that is not a DOMException InvalidAccessError: property "code" is equal to undefined, expected 15
      

  • OK /IndexedDB/idbobjectstore_getAll.any.html (#39276)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/idbobjectstore_getAll.any.worker.html (#39400)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/key-conversion-exceptions.any.html (#39305)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • OK /IndexedDB/key-conversion-exceptions.any.worker.html (#39284)
    • FAIL [expected PASS] subtest: IDBCursor continue() method with throwing/invalid keys

      assert_throws_exactly: key conversion with throwing getter should rethrow function "() => {
            receiver[method](key);
          }" threw object "TypeError: receiver[method] is not a function" but we expected it to throw object "getter: throwing from getter"
      

  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resize_event.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • TIMEOUT [expected PASS] /_mozilla/shadow-dom/move-element-with-ua-shadow-tree-crash.html (#39473)
  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 1

      assert_equals: expected "80px" but got "38.3166666666667px"
      

    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 2

      assert_equals: expected "80px" but got "38.3166666666667px"
      

  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted serif (drawing text in a canvas)

      assert_equals: quoted serif matches  @font-face rule expected 125 but got 40
      

  • TIMEOUT [expected FAIL] /dom/xslt/encoding-single-chunk.html (#39983)
  • TIMEOUT [expected FAIL] /dom/xslt/large-cdata.html (#38029)
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-dest
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-site destination
  • OK /html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html (#29066)
    • PASS [expected FAIL] subtest: Check execution order on load handler
    • PASS [expected FAIL] subtest: Check execution order from nested timeout
  • OK [expected TIMEOUT] /html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.html (#30970)
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html (#29087)
  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected TIMEOUT] subtest: Element with tabindex should support autofocus

      assert_equals: expected "SPAN" but got "BODY"
      

    • PASS [expected NOTRUN] subtest: Non-HTMLElement should not support autofocus
    • TIMEOUT [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic test (normal form)

      assert_equals: expected "basic=test" but got ""
      

  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730, #39631)
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • FAIL [expected PASS] subtest: Reload domComplete > Original domComplete

      assert_true: Reload domComplete > Original domComplete expected true got false
      

    • FAIL [expected PASS] subtest: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd

      assert_true: Reload domContentLoadedEventEnd > Original domContentLoadedEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload domContentLoadedEventStart > Original domContentLoadedEventStart

      assert_true: Reload domContentLoadedEventStart > Original domContentLoadedEventStart expected true got false
      

    • PASS [expected FAIL] subtest: Reload domInteractive > Original domInteractive
    • FAIL [expected PASS] subtest: Reload fetchStart > Original fetchStart

      assert_true: Reload fetchStart > Original fetchStart expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventEnd > Original loadEventEnd

      assert_true: Reload loadEventEnd > Original loadEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventStart > Original loadEventStart

      assert_true: Reload loadEventStart > Original loadEventStart expected true got false
      

  • OK /preload/preload-error.sub.html (#37177)
    • PASS [expected FAIL] subtest: success (fetch): main
    • FAIL [expected PASS] subtest: 404 (fetch): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.xml?pipe=status%28404%29&label=fetch should be loaded expected a number greater than 0 but got 0
      

  • OK /trusted-types/trusted-types-navigation.html?01-05 (#38975)
    • PASS [expected FAIL] subtest: Navigate a window via anchor with javascript:-urls in enforcing mode.
Stable unexpected results (3)
  • OK /workers/WorkerGlobalScope_importScripts_NetworkErr.htm
    • PASS [expected FAIL] subtest: importScripts() with non-existent script file
  • OK /workers/importscripts_mime.any.worker.html
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: text/html is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: text/plain is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: application/xml is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: application/octet-stream is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: text/potato is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: potato/text is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: aaa/aaa is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: zzz/zzz is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: Text/html is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types: text/Html is blocked.
    • And 2 more unexpected results...
  • OK /workers/importscripts_mime_local.any.worker.html
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: text/html is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: text/plain is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: application/xml is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: application/octet-stream is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: text/potato is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: potato/text is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: aaa/aaa is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: zzz/zzz is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: Text/html is blocked.
    • PASS [expected FAIL] subtest: importScripts() requires scripty MIME types for data: URLs: text/Html is blocked.
    • And 16 more unexpected results...

@github-actions
Copy link
Copy Markdown

⚠️ Try run (#18652601184) failed.

@jdm
Copy link
Copy Markdown
Member

jdm commented Oct 20, 2025

@WaterWhisperer
Copy link
Copy Markdown
Contributor Author

Can you update the expected test results to account for the new passing tests? https://book.servo.org/hacking/testing.html#updating-web-platform-test-expectations

Okay, I'll do it.

@Gae24
Copy link
Copy Markdown
Contributor

Gae24 commented Oct 20, 2025

There is a fail now:

FAIL [expected PASS] importScripts() requires scripty MIME types for blob: URLs: text/javascript;bla;bla is allowed

the same test case pass when using a data url

@WaterWhisperer
Copy link
Copy Markdown
Contributor Author

There is a fail now:

FAIL [expected PASS] importScripts() requires scripty MIME types for blob: URLs: text/javascript;bla;bla is allowed

the same test case pass when using a data url

This test remains failing. I investigated and found the cause:

For blob: URLs with malformed MIME parameters (e.g., text/javascript;bla;bla without proper key=value format), the MIME parsing in filemanager_thread.rs:304 fails and falls back to text/plain:

buf.type_string.parse().unwrap_or(mime::TEXT_PLAIN)

This causes the Content-Type header to be set to text/plain instead of preserving the essence text/javascript, which the test expects to be accepted despite the malformed parameters.

In contrast, data: URLs work correctly because the data_url crate normalizes MIME types before they reach our validation.

Should we:

  1. Make the MIME parsing in filemanager_thread.rs more lenient (extract essence even with malformed parameters)?
  2. Or update the test expectation to reflect the current behavior?

See try build results.

@jdm
Copy link
Copy Markdown
Member

jdm commented Oct 21, 2025

We've been switching code to use data_url's MIME parser: #36522 and #27725 are examples.

@jdm
Copy link
Copy Markdown
Member

jdm commented Oct 21, 2025

That being said, I think doing that switch in a separate PR would make sense.

@WaterWhisperer WaterWhisperer force-pushed the workerglobalscope-importscripts branch from 2030319 to cc29526 Compare October 21, 2025 05:10
@WaterWhisperer
Copy link
Copy Markdown
Contributor Author

Thanks for the guidance! I've updated the WPT expectations to mark the blob: URL test with malformed MIME parameters (text/javascript;bla;bla) as expected FAIL.

As you mentioned, switching to data_url::Mime should be done in a separate PR. This PR focuses on the core requirements of issue #39993 - adding response status and MIME type validation to importScripts().

Try build

Copy link
Copy Markdown
Member

@jdm jdm left a comment

Choose a reason for hiding this comment

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

Looks great! Thanks!

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Oct 21, 2025
@jdm jdm enabled auto-merge October 21, 2025 05:38
@jdm jdm added this pull request to the merge queue Oct 21, 2025
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Oct 21, 2025
Merged via the queue into servo:main with commit 51b0ecc Oct 21, 2025
30 checks passed
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Oct 21, 2025
@WaterWhisperer WaterWhisperer deleted the workerglobalscope-importscripts branch October 21, 2025 07:14
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.

WorkerGlobalScope's importScripts should check response

5 participants