Skip to content

Repository files navigation

URLScission

CI Swift Package Manager Platforms License

See, mock, block, and modify network traffic on Apple platforms — a framework of pluggable interception methods sharing one engine. Adopt it in your own app to log and mock your URLSession traffic, or (on a jailbreak) inject it into another app to inspect and rewrite its requests across every networking stack.

URLScission started as a URLSession log + mock framework. It is now organised around a small engine and a set of interception methods, each of which advertises what it can do (observe / block / mock / modify / replay), so the same rules and log sink work whether traffic goes through URLSession, CFNetwork, Network.framework, or raw TLS.

Features

  • Block requests by URL substring or regex, with a configurable status code.
  • Mock responses — inline (from a RuleConfig) or file-based (the original .mock file / MockStorage workflow), with status, headers, and a UTF-8 or base64 body.
  • Rewrite requests in flight: set/remove headers, or rewrite the URL.
  • Rewrite responses: after the real load completes, override status, headers, and/or body — the request still reaches the network.
  • Response inspector: the URLProtocol path captures a full InterceptedExchange (request and response, headers + bodies), not just a one-line log — so a log sink or the PeerWall daemon can show and replay a complete call.
  • Adopt vs. inject vs. injectFull — link the dependency-free core into your own app, or (on a jailbreak) inject observe-only hooks into another app's CFNetwork/Network.framework/TLS/CFURL layers, optionally adding the re-issuing URLProtocol intercept for full block/mock/modify there too.
  • Live rule updates: RuleConfig is a Codable JSON wire format; the injected build's DaemonRuleSource polls it from the PeerWall daemon and swaps rules in without restarting the target process.

Two products

Product Dependencies Use
URLScission none (Foundation only) Link it in your app. Full observe/block/mock/modify of your URLSession traffic via a URLProtocol. App-Store-safe.
URLScissionInject ellekit / a function-hooker (runtime) Inject the engine into another app on a jailbreak. Adds the CFNetwork / Network.framework / TLS / CFURL methods and a daemon transport.

The split is a clean seam: the core defines a FunctionHooker protocol but ships no hooker, so on its own it performs only the dependency-free URLProtocol methods. URLScissionInject supplies an ellekit-based hooker and the lower-level methods. No jailbreak code ever ships in the core framework.

Interception methods & capabilities

Method Layer observe block mock modify req modify resp
defaultURLProtocol URLSession .default/.shared
allURLSessionConfigs URLSession custom/ephemeral
cfNetwork CFNetwork request APIs
networkFramework nw_connection_send
tls boringssl SSL_write
urlObject CFURL/NSURL construction

A URLProtocol is a loader replacement, so it has full capability. The lower-level methods are observation points (and can block by failing the call); byte-stream response rewriting there is out of scope. WebKit page loads over HTTP/3 are not interceptable — the URL never leaves WebKit's C++/QUIC path; see ARCHITECTURE.md.

InterceptionMethod bundles these into three presets you pass to start(methods:):

Preset Methods Use
.adopt defaultURLProtocol + allURLSessionConfigs Your own app. Full capability, dependency-free.
.inject cfNetwork + networkFramework + tls + urlObject Injected into another app. Observe-only — deliberately excludes the URLProtocol methods, which would re-issue every request as a fresh real load and double the host app's traffic.
.injectFull every method above, incl. defaultURLProtocol/allURLSessionConfigs Injected, but you also want block/mock/modify inside the target — accepting the re-issue cost.

Install

Swift Package Manager

.package(url: "https://github.com/inso1337/URLScission.git", from: "1.0.0")

Then depend on URLScission (in-app) and/or URLScissionInject (jailbreak).

CocoaPods

pod 'URLScission'

Quick start — in your own app

import URLScission

// Observe + control your app's URLSession traffic.
URLScission.start(methods: .adopt)

// Block trackers:
URLScission.rules.blockPatterns = ["doubleclick.net", "google-analytics.com"]

// Mock an endpoint (existing .mock files keep working):
URLScission.mockStorage.loadMockFiles(in: Bundle.main.resourceURL!)
URLScission.mockStorage.activeMockIdentifiers = ["MyMock"]

// Rewrite requests:
URLScission.rules.addRewrite(
    RequestRewrite(pattern: "api.example.com", setHeaders: ["X-Debug": "1"]))

// Rewrite responses (request still reaches the network; only what comes back
// is altered):
URLScission.rules.responseRewrites.append(
    ResponseModification(pattern: "api.example.com/flag", status: 200,
                         body: Data(#"{"enabled":true}"#.utf8)))

// Or load block/mock/rewrite rules in bulk from a RuleConfig (the same JSON
// wire format the control channel uses):
let config = RuleConfig(block: ["ads.example.com"],
                        mocks: [.init(urlContains: "api.example.com/me", status: 200,
                                     body: #"{"id":1}"#)])
URLScission.rules.apply(config)

Captured requests and responses (status, headers, body, content-type) flow to the engine's log sink; supply your own by conforming to InterceptionLogSink. The URLProtocol methods additionally emit a full InterceptedExchange (request + response, headers + bodies) via record(exchange:), so a sink can show or replay the whole call rather than just log two lines.

Inject into another app (jailbreak)

import URLScissionInject

// From an injected dylib's constructor:
URLScissionInject.start(methods: .inject)       // observe-only: CFNetwork + nw + TLS + CFURL
// URLScissionInject.start(methods: .injectFull) // ...plus the re-issuing URLProtocol intercept,
                                                  // for block/mock/modify inside the target too.

Records stream to the PeerWall daemon over TCP loopback as newline-framed JSON (one object per exchange, bodies base64-encoded); rules flow back the same way via DaemonRuleSource, polled on an interval and swapped in without restarting the target process. See ARCHITECTURE.md for the injection design.

Requirements

  • iOS 12+, macOS 10.13+, tvOS 12+, watchOS 4+
  • Swift 5.5+

License

MIT — see LICENSE.

About

URLSession Log and Mock Framework

Resources

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages