Skip to content

v0.0.3b

Pre-release
Pre-release

Choose a tag to compare

@steck0714 steck0714 released this 03 Aug 00:05
9dd8eea

Summary of Implementation

  • frame_origin.py (New Module) — FrameOriginTracker
    • The Python side (the completely zero-trust side that does not trust JS at all) traverses the frame tree using page.mainFrame() / QWebEngineFrame.children().
    • It distributes an unguessable token to each frame via runJavaScript(), ensuring it is delivered only to that specific frame.
    • Other frames cannot read this token due to the Same-Origin Policy.
    • Retraversal is triggered by the navigationRequested signal (discovered in 0.0.3a0), with a periodic 2-second interval scan running in parallel as a safeguard.
  • Token Integration Across Slots
    • Added a frame_token parameter to all 16 @Slot methods (ranging from listDevices to various transfer-related methods).
    • Routed the token through _get_open_device(), a bottleneck method that almost all other methods pass through.
    • This fully operationalized the feature, ensuring that "a handle opened by a subframe can continuously and exclusively be used by that subframe."
  • Core Design Rule
    • When wiring the tracker, origins are resolved strictly via tokens without exception, including the main frame.
    • We intentionally left no loopholes like "an empty token defaults to the main frame."
    • Allowing such a loophole would let an adversarial subframe impersonate the main frame simply by invoking the raw QWebChannel directly with an empty token.

Pitfalls & Fixes

Verified on an actual QWebEnginePage, discovering and resolving three implementation pitfalls:

  • runJavaScript() Error
    • Passing only the code string as a single argument throws a "not enough arguments" error. A callback function is strictly required.
  • setHtml() URL Behavior
    • setHtml(html, baseUrl=...) fails to reflect the baseUrl in QWebEngineFrame.url(), treating it as a data: URL instead. Using page.load() works correctly.
  • QWebEngineFrame Identity Issue
    • The QWebEngineFrame object returns a brand-new wrapper every time children() is called.
    • This makes frame identification via id() completely unreliable. We removed the dependency on unique IDs and resolved this by issuing fresh tokens on every scan while enforcing an upper limit on the total token count.

Testing

  • test_frame_origin.py (New, 9 test cases)
    • Includes 8 fast tests using mocks.
    • Includes 1 integration test using a real QWebEnginePage and cross-origin iframes.
  • test_bridge.py Updates (2 test cases added)
    • Added tests for token spoofing rejection and handle isolation between origins.
  • Legacy Test Maintenance
    • Updated existing Python/JS tests to support the new signature (updating 16 instances of the fakeBridge mock).

Note: Since we cannot guarantee stable operation under all conditions yet, please wait for the next version release.