monetization-sdk-integration — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited monetization-sdk-integration (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Sources/<AdsBridge>/)import GoogleMobileAds over here too" — IMMEDIATE invokeSkip when: changing pure values / protocols inside your monetization core target (no third-party touch).
Default rule: no third-party SDKs in the app. Apple-platform native APIs preferred (OSLog over Sentry, MetricKit over Firebase Crashlytics, GameKit over Steam-style backend, etc.).
Break-glass exception is granted when ALL of:
canImport) — macOS / Catalyst paths must build without the SDKIf any of (1)-(5) fails: deny. Reject the SDK proposal; suggest Apple-native fallback or sit it out.
For every accepted SDK:
<SdkName>Bridge.swift defines the seam. Plain Swift; no SDK import.Live<SdkName>Bridge.swift is the ONLY file allowed to import <SDKModule>.any <SdkName>Bridge for DI. Test seam: Fake<SdkName>Bridge in the matching test target (e.g. Tests/<AdsBridge>Tests/FakeAdMobBridge.swift).Example for AdMob (currently shipped):
Sources/<AdsBridge>/AdMobBridge.swift — protocol (your monetization target's bridge protocol file)Sources/<AdsBridge>/LiveAdMobBridge.swift — sole import GoogleMobileAds siteTests/<AdsBridge>Tests/FakeAdMobBridge.swift — test seamCanonical regex (matches Swift 6 access-level imports too):
# replace <SDKModule> / <your-sources-root> with your actual values
rg '^(internal |private |public |@_implementationOnly |@preconcurrency )*import <SDKModule>' <your-sources-root>Expected count: 1 (live bridge file).
If > 1: the contract is broken. Either consolidate behind the existing bridge OR file an exception in docs/foundations.md documenting WHY a second import site is necessary (with prior reviewer sign-off).
Documentation references:
docs/foundations.md — the contract text itselfiOS-only SDKs (AdMob/UMP/most monetization stack):
#if canImport(GoogleMobileAds)
import GoogleMobileAds
// ... live impl code
#else
// macOS / catalyst fallback — usually NoOp returning sensible empty values
#endifThe Package.swift dep arrow itself must also gate the SDK to iOS:
.product(
name: "GoogleMobileAds",
package: "swift-package-manager-google-mobile-ads",
condition: .when(platforms: [.iOS])
),Without condition:, macOS build fails on swift build because Google ships iOS-only xcframeworks.
A test target for the bridge ships Fake<SdkName>Bridge (actor or class). All unit tests inject the fake; real SDK only loaded at runtime via DI in the app's composition root. A shared testing-scaffolding target may ship shared fakes used across both ad and IAP test targets (e.g. FakeAdProvider, FakeIAPClient); the per-SDK bridge fakes live in their own test target.
Fake must:
Sendable (Swift 6 actor or @unchecked Sendable + lock-guarded)script(...) or per-call setter API for deterministic test outcomesGADBannerView → BannerView, GADRequest → Request, etc.bannerAdUnitID constant used fatalError("REPLACE_BEFORE_RELEASE: …") rather than a placeholder string — prevents accidental Release build silently serving test creatives against production app IDGADApplicationIdentifier + bridge constant are always updated togetherGADBannerView SwiftUI host shipped. import GoogleMobileAds stays confined to the live bridge file; the live banner crosses into the UI layer via BannerViewProviding.bannerView(for:) -> AnyView? — an `AnyView` (SwiftUI), never a GoogleMobileAds type — so UI targets import zero SDK. One shared BannerSlotView replaced per-app placeholder slots.#if DEBUG forces Google's universal test unit; Release reads the per-app prod id from Bundle.main via xcconfig.import GoogleMobileAds ungated → macOS build brokecanImport(GoogleMobileAds) + Package.swift condition: .when(platforms: [.iOS]) + macOS fallback uses NoopAdProvidermise exec invocationslefthook.yml pre-commit.parallel: false makes hook timing predictableswift build on a Mac, blocking PRs.fatalError guard or build-config injection; hard-coded prod IDs in DEBUG/Release pivot risk accidental Release ship with wrong combination.When proposing a new SDK, fill this in:
SDK: <name + version + GitHub URL>
Capability: <what it does that Apple-native can't>
Privacy domains: <list — must match PrivacyInfo.xcprivacy>
iOS-only / cross-platform: <iOS-only | iOS+macOS | etc.>
Bundle size impact: <KB / MB>
Tracking ATT required: <yes/no>
UMP consent required: <yes/no>
Test seam plan: <how Fake<SdkName>Bridge will look>
Isolation audit grep target count: <expected 1>
Fallback platform behaviour: <Noop / throw / etc.>If any field is "TBD" or "?", do NOT proceed — research first.
docs/foundations.md — the no-3rd-party rule + break-glass exception + isolation contract textSources/<AdsBridge>/<SdkName>Bridge.swift — protocol seam exampleSources/<AdsBridge>/Live<SdkName>Bridge.swift — single-import-site example<App>/Resources/PrivacyInfo.xcprivacy — tracking domains declaration~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.