Menu

Security

Security

Reporting a vulnerability

Email support@eonix.lk with "DevKit security" in the subject. Please include what you did, what happened, and the version from Settings → About.

We aim to acknowledge within 2 business days and to ship a fix or a mitigation within 30 days for anything that puts user data at risk. There is no bug bounty. If you would like credit in the release notes, say so and we will include it.

Please do not open a public issue for a security problem before we have had a chance to respond.

What DevKit's security model actually is

DevKit's attack surface is unusually small, and most of that is by construction rather than by policy:

  • No network. DevKit makes no network requests. On macOS it does not request com.apple.security.network.client, so the sandbox would refuse one. You can verify this by using the app with the device in Airplane Mode: everything works.
  • No account, no server, no secrets. There is nothing to sign in to, no API key to store and no credential in the app. It therefore ships no Keychain code and no keychain entitlement.
  • No third-party code. Zero dependencies. Package.resolved has no pins, and CI fails if one ever appears.
  • No analytics, no tracking, no crash SDK. NSPrivacyTracking is false and the collected-data list is empty.

What is stored, and where

DataLocationLeaves the device?
History (tool, time, truncated previews)SwiftData, on deviceOnly to the user's own private CloudKit database, and only if they are Pro and turn sync on
FavouritesSameSame
Drafts (unsent input)SwiftData, local configurationNever, deliberately: a half-typed private key should not sync because iCloud is on
PreferencesApp-group UserDefaultsNo
Shared items from the extensionA directory of JSON files in the app groupNo

History previews can be turned off in Settings, after which a run stores only the tool, the time and a SHA-256 of the input. "Erase all data" removes everything, including the CloudKit records.

The parts that handle hostile input

Everything a user pastes is untrusted. The parsers are written accordingly:

  • Bounded. A 5 MB ceiling on tool input, checked before any allocation. Recursive parsers stop at 100 levels (JSON) and 32 levels (DER), because tools run on an actor whose stack is smaller than the main thread's.
  • Total. Every failure is a typed ToolError with a message and a recovery suggestion. There are no force unwraps and no try! in shipped code, and CI fails if one appears.
  • Time-bounded where the algorithm is not. The regex tester abandons a run after two seconds and says the pattern is probably backtracking, rather than wedging a thread.
  • Fuzzed by contract. Every one of the 42 tools is run against empty input, control characters, emoji, 100 KB of one character, deeply nested structures, path-traversal strings, script tags, SQL injection strings and 5,000 combining marks, plus every other tool's sample input. Anything but a ToolError fails the build.

The web view

DevKit contains exactly one WKWebView, in the HTML preview, with four independent defences:

  1. JavaScript disabled through defaultWebpagePreferences.allowsContentJavaScript.
  2. Content loaded with loadHTMLString(_:baseURL: nil), so relative URLs resolve to nothing.
  3. A WKContentRuleList that blocks every resource load ("url-filter": ".*"block). The preview refuses to render until the rule list has compiled, so there is no window in which HTML could load without it.
  4. A navigation delegate that cancels every navigation after the initial load.

The data store is non-persistent, so nothing the preview touches survives the screen.

Extensions

The share extension and the widgets are sandboxed, hold only the app-group entitlement, and have no network entitlement. The share extension never calls extensionContext.open and never reaches for UIApplication: it does its work in its own process and leaves results in the app group.

Supported versions

VersionSupported
1.0.xYes

Security fixes ship in the current release. There is no long-term support branch.