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.
- Block requests by URL substring or regex, with a configurable status code.
- Mock responses — inline (from a
RuleConfig) or file-based (the original.mockfile /MockStorageworkflow), 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
URLProtocolpath captures a fullInterceptedExchange(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
URLProtocolintercept for full block/mock/modify there too. - Live rule updates:
RuleConfigis a Codable JSON wire format; the injected build'sDaemonRuleSourcepolls it from the PeerWall daemon and swaps rules in without restarting the target process.
| 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.
| 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. |
.package(url: "https://github.com/inso1337/URLScission.git", from: "1.0.0")Then depend on URLScission (in-app) and/or URLScissionInject (jailbreak).
pod 'URLScission'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.
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.
- iOS 12+, macOS 10.13+, tvOS 12+, watchOS 4+
- Swift 5.5+
MIT — see LICENSE.