Skip to content

fix(privacy): stop matching ads inside ordinary identifiers - #33

Open
Maher-Reven wants to merge 1 commit into
RevylAI:mainfrom
Maher-Reven:fix-admob-false-positive
Open

Maher-Reven wants to merge 1 commit into
RevylAI:mainfrom
Maher-Reven:fix-admob-false-positive

Conversation

@Maher-Reven

@Maher-Reven Maher-Reven commented Sep 19, 2026

Copy link
Copy Markdown

Fixes #30.

Problem

internal/privacy/scanner.go matched Google Ads/AdMob with:

{regexp.MustCompile(`(?i)(google.*ads|GADMobileAds|admob)`), "Google Ads/AdMob"},

google.*ads is case-insensitive and unanchored, so ads matches inside ordinary identifiers — loadSdk, downloads, uploads, threads. One line of Google Sign-In is enough:

GoogleSignin: Awaited<ReturnType<typeof loadSdk>>["GoogleSignin"],

GoogleloadSdk → the adS in loadSdk completes the match.

I reproduced it with a fixture containing that single line plus a PrivacyInfo.xcprivacy with NSPrivacyTracking = false, and no ad SDK anywhere:

$ greenlight privacy ./repro-app
  Tracking SDKs found: Google Ads/AdMob
  [CRITICAL] §5.1.2 Tracking SDKs detected without ATT implementation
  NOT READY — 1 critical privacy issue(s)

This is the top severity, so preflight --exit-code returns ErrThreshold and fails the build for an app that would pass review — and the attached fix tells the developer to add requestTrackingAuthorization(), which is itself a §5.1.2 risk in an app that does not track.

Fix

Require an ad-specific token:

{regexp.MustCompile(`(?i)(googlemobileads|google-mobile-ads|GADMobileAds|GADApplicationIdentifier|\badmob)`), "Google Ads/AdMob"},

\badmob is anchored at the start only, deliberately: \badmob\b would miss AdMobBanner and AdMobInterstitial, which is what expo-ads-admob actually exports. Anchoring only the front still rejects any word that merely ends in those letters.

After the fix the same fixture reports GREENLIT — privacy compliance looks good.

Testing

Two tests in internal/privacy/scanner_test.go, in both directions:

  • TestGoogleAdsPatternIgnoresOrdinaryIdentifiers — the four identifier shapes above. All four fail against the old pattern, which I verified by reverting it.
  • TestGoogleAdsPatternStillDetectsRealIntegrations — six real integrations that must keep matching: import GoogleMobileAds, GADMobileAds.sharedInstance(), GADApplicationIdentifier, react-native-google-mobile-ads, expo-ads-admob, and a bare admob require.

The second test caught a mistake while I was writing this. My first version also asserted detection in Info.plist, Podfile, package.json and build.gradle, and those cases failed — detectLang only returns a language for .swift, .m, .h, .ts, .tsx, .js, .jsx, so manifest and Gradle files are never scanned for tracking SDKs at all. That is pre-existing behaviour and out of scope here, but it means an AdMob integration declared only in package.json or a Podfile goes undetected today. Worth a separate issue if you would like one. I dropped the Android-only alternations I had added for the same reason — .java/.kt/.gradle are never read, so they could never match.

go build ./... and go test ./... are clean.

Scope note

The two neighbouring patterns have the same shape and the same defect — adjust.*sdk matches adjustsFontSizeToFitWidth and adjustedContentInset whenever sdk appears on the line, and unity.*ads matches unityWebView.loadThreads(). I have kept this PR to the reported issue; those are in a follow-up so each can be reviewed on its own.


Note

Low Risk
Scoped regex change in privacy SDK detection with bidirectional tests; reduces false CRITICAL findings without touching auth or data paths.

Overview
Tightens the privacy scanner’s Google Ads/AdMob detection so apps without ad SDKs no longer get false §5.1.2 ATT findings (and preflight --exit-code failures).

The old google.*ads alternation is replaced with ad-specific tokens (googlemobileads, google-mobile-ads, GADMobileAds, GADApplicationIdentifier, and \badmob with a leading word boundary only so AdMobBanner still matches). Two regression tests cover benign Google Sign-In / loadSdk-style lines and common real AdMob integration shapes in scanned Swift, ObjC, and JS/TS files.

Reviewed by Cursor Bugbot for commit 9d61ff0. Bugbot is set up for automated code reviews on this repo. Configure here.

`(?i)(google.*ads|GADMobileAds|admob)` is unanchored, so `ads` matched
inside `loadSdk`, `downloads`, `uploads` and `threads`. Any app that used
a non-ad Google SDK and had one of those words on the same line got a
CRITICAL §5.1.2 finding, which `--exit-code` turns into a failed build,
and the suggested fix was to add an ATT prompt the app does not need.

Require an ad-specific token instead. `\badmob` is anchored only at the
start so it still covers `AdMobBanner` and `expo-ads-admob` while
rejecting words that merely end in those letters.

Fixes RevylAI#30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive: google.*ads matches loadSdk — CRITICAL ATT finding on apps with no ad SDK

1 participant