Skip to content

Insights

Privacy Map

Since 0.2.1 Requires Cloak Proxy 0.2.1 or later.

See which outside companies an app sends its users' data to, what each receives (email, phone, location, IDs, tokens) and where, with privacy label hints.

Privacy Map shows where one app’s users’ data goes. For every outside company the app talks to, such as analytics, attribution, advertising and crash-reporting services, it lists the personal data and identifiers that company received: email addresses, phone numbers, names, locations, advertising and device IDs, user IDs, the app’s own auth tokens, and IDs the app’s own servers issued, such as order numbers. For each one it shows exactly where in the request it was sent.

Use it to review what your app’s SDKs collect before a release, to fill in the App Store privacy label or the Google Play Data safety form, or to catch an SDK that receives something it shouldn’t, such as a login token.

  1. Capture the app while you use it, with SSL Proxying on for it, so its HTTPS requests are decrypted. Sign up, log in and go through the screens you want to check.

  2. Open Privacy Map:

    • Choose Tools › Privacy Map… (⌥ ⌘ P). If an app is selected in the sidebar, the map opens for it.
    • Or right-click the app in the sidebar and choose Privacy Map for app….
    • On Linux, press Ctrl Alt P, or right-click a request from the app and choose Privacy Map for app….
  3. If needed, choose the app from the App menu. The map is built right away.

  4. Check Own domains. Remove domains that aren’t the app’s, and add any of its servers that are missing.

  5. Optionally, under Test account, type the Email, Phone and Name you tested with and press Return, so Cloak Proxy finds them wherever they were sent.

  6. Read the summary, then the details for each company. Click a company or a request count to select those requests in the traffic list.

One app's requests to outside companies, colored by the most sensitive data each received.

  1. The app to map. Rescan to include new traffic.
  2. The app's own domains. Click × to treat one as a third party, or add one.
  3. Your test account: found wherever it was sent.
  4. Fix: an outside company received the app's auth token or a card number.
  5. Each company that received data, with what. Click to select its requests.
  6. Mark as the app's own: stop treating this domain as a third party.
Privacy Map for ShopApp, with shopapp.example treated as its own: ShopApp sends personal data or identifiers to 5 of the 6 outside companies it talks to, a red banner saying sdk-collector.example received the app's auth token, summary chips per data type, a map from ShopApp to five companies with colored lines, and below, what each company received and where, with partly hidden values
Control What it does
App The app to map. Lists every app in the captured traffic except Cloak Proxy itself.
Rescan Builds the map again from the traffic captured so far.
Copy Report Copies the map as Markdown. Reads Copied until the map changes. Only when the app talked to at least one outside company.
Own domains The domains that belong to the app. Everything else is an outside company. Click × on a domain to treat it as a third party.
Add a domain Type one of the app’s domains and press Return. It’s reduced to its registrable domain, so api.shopapp.example adds shopapp.example.
Test account (optional): Email, Phone, Name Values to look for in every request to outside companies. Press Return in any of them to save them and rescan.

The map is rebuilt whenever you choose another app, change the app’s domains or press Return in a test account field. It doesn’t update by itself as new traffic arrives; click Rescan.

Privacy Map reads decrypted requests only. If the app’s HTTPS isn’t decrypted yet, the window says how many encrypted connections it made and offers Decrypt app’s HTTPS, which turns on SSL Proxying for every domain the app connects to. Use the app again, then click Rescan.

Privacy Map for an app whose HTTPS isn't decrypted yet.

  1. Turns on SSL Proxying for every domain the app connects to
Privacy Map for ShopApp with a lock and the message ShopApp's requests are encrypted: ShopApp made 1 HTTPS connection, but Privacy Map can only read decrypted requests. Below it, the button Decrypt ShopApp's HTTPS

When the app has no requests, the window says No requests from app: use the app with Cloak Proxy capturing, then rescan.

Privacy Map needs to know which servers are the app’s, so it can tell what the app’s backend sends from what outside companies receive. It works with registrable domains, such as shopapp.example for api.shopapp.example and cdn.shopapp.example.

The first time you map an app, Cloak Proxy suggests its domains:

  • Domains whose name shares a word of 4 or more letters with the app’s name or bundle identifier (ignoring words such as com, app, ios, mobile, client and prod), or contains the app’s name. ShopApp’s requests to api.shopapp.example make shopapp.example its own.
  • Domains you captured for the app because it ignores the proxy.

Domains of known SDK companies are never suggested. Once you change the list, by removing a domain, adding one or clicking Mark as app’s Own, Cloak Proxy remembers it for that app.

Personal data in SDK payloads doesn’t always sit under a recognizable key. Type the email, phone number and name of the account you used while capturing, and Privacy Map finds them in any value sent to an outside company:

Field Matches
Email The address anywhere in a value, ignoring case.
Phone The last 10 digits of the number anywhere in a value. Needs at least 7 digits.
Name The name anywhere in a value, ignoring case. Needs at least 3 characters.

Cloak Proxy remembers these values per app, in its preferences on this Mac. Use a test account, not your own details.

Part Shows
Headline App sends personal data or identifiers to N of the M outside companies it talks to, or that none was found.
Request count N of M requests from app went to outside companies.
Fix: banner In red, when an outside company received the app’s auth token or a card number: Nothing outside your own servers should get these.
Chips One per kind of data, in its severity color, followed by → and the companies that received it.

Each kind of data has a severity, shown by color on the chips, the map’s lines and the details:

Color Meaning Data
Red Fix it: no outside company should get this Your app’s auth token, card number
Orange Personal data: declare it, and check the company needs it Email address, phone number, name, precise location
Blue Identifiers that link activity to a user or device Approximate location, advertising ID, device ID, user ID, IDs from your backend

On the left, the app with its own servers. On the right, under Outside companies that received data, a card for each company that received something, most sensitive first, then by how many findings and how many requests. Each card shows:

  • The company’s name and category, such as Analytics or Attribution, when Cloak Proxy knows it; otherwise the domain.
  • How many requests went to it, and up to three of its hosts.
  • A chip for each kind of data it received.

The line from the app to each card takes the color of the most sensitive data that company received, and is thicker for red. Under the cards, Also talked to, with no personal data found: lists the other outside companies. Click a card to select every request that sent it data.

Below the map, a section per company lists every finding:

Column Shows
Data The kind, in its severity color.
Where The exact place in the request: query phone, header X-User, cookie uid, body $.events[*].user_properties.email, body email. Array positions are shown as [*].
From For values the app’s own servers issued or the app authenticates with: where they came from, such as From POST api.shopapp.example/v1/orders response $.data.user.id, or From the Authorization header of POST api.shopapp.example/v1/orders.
Example The value, partly hidden.
N req How many requests sent it there. Click to select them in the traffic list.

Mark as app’s Own next to a company moves its domain into the app’s own domains and rescans. Use it for the app’s servers on another domain, such as a separate API or CDN domain.

For your privacy label lists each kind of data sent to outside companies (except auth tokens, which are a problem to fix rather than something to declare), the App Store privacy label category it usually falls under, and the companies that received it:

Data App Store category
Card number Financial Info › Payment Info
Email address Contact Info › Email Address
Phone number Contact Info › Phone Number
Name Contact Info › Name
Precise location Location › Precise Location
Approximate location Location › Coarse Location
Advertising ID Identifiers › Device ID
Device ID Identifiers › Device ID
User ID Identifiers › User ID
IDs from your backend Depends on what it identifies (often Purchases or Other Data)

Privacy Map reads every request from the app (except encrypted tunnels) and splits them into requests to the app’s own domains and requests to everyone else.

First, it learns from the app’s own traffic:

  • The app’s auth tokens in its requests. The value of each Authorization header, any header with token in its name, and X-API-Key, without a Bearer, Token or Basic prefix, when it’s at least 12 characters.
  • Values in its JSON responses that look like IDs: at least 4 characters with no spaces, containing a digit or at least 20 characters long, and not a timestamp, a date or a version number. Each one is sorted by its key:
    • under keys such as access_token, refresh_token, id_token, jwt, session_token and api_key: Your app’s auth token;
    • under keys such as user_id, customer_id, account_id, member_id, distinct_id, external_id and profile_id: User ID;
    • anything else: IDs from your backend. Order numbers, cart IDs and payment references land here.

Any of these values sent to an outside company is a finding, with where it came from. A value of more than 24 characters is also checked piece by piece, so a token inside a URL or a longer string is found too.

Then it checks every value in each request to an outside company: query parameters, the path, headers (except standard ones such as Host, User-Agent, Accept, Content-Type, Origin and Referer), each cookie, and the body. JSON bodies are read field by field, including JSON packed inside a string (common in analytics SDKs) and JSON inside a form field. Form bodies are read field by field, and other text bodies as a whole.

Most kinds are found by the key name together with the shape of the value. Key names ignore case and punctuation, so customer_user_id, customerUserId and CUSTOMER-USER-ID are the same key.

Data Found when
Your app’s auth token A value matches one of the app’s own tokens.
Card number Any value contains a 13 to 19 digit number that passes the Luhn check.
Email address A key such as email, email_address, mail or user_email holds an email address; or any value contains one. Outside email keys, mailboxes such as noreply@, support@ and info@, and addresses at example.com, are ignored.
Phone number A key such as phone, phone_number, mobile, msisdn or tel holds 7 to 15 digits with only +, -, spaces and parentheses besides; or any value contains a + followed by 10 to 14 digits.
Name A key such as first_name, last_name, full_name, display_name, given_name or family_name holds 2 to 80 characters with a letter and no @.
Precise location A lat or latitude key has a lng, lon, long or longitude key next to it in the same JSON object, with valid coordinates and at least 3 decimal places.
Approximate location The same, with fewer decimal places.
Advertising ID A key such as idfa, advertising_id, gaid, adid or aaid holds a UUID that isn’t all zeros.
Device ID A key such as idfv, vendor_id, device_id, android_id, install_id or installation_id holds at least 8 characters.
User ID A user-ID key, as above, holds a value other than 0, -1, anonymous or guest; or a value matches a user ID the app’s servers issued.
IDs from your backend A value matches an ID the app’s servers issued. When the same value is sent under a more specific key, such as user_id, it’s listed as that kind instead, with where it came from.

Your test account’s email, phone and name are found anywhere, whatever the key.

Cloak Proxy recognizes the domains of more than 40 common SDK companies and shows their name and a category: Advertising, Analytics, Attribution, Engagement, Push notifications, Crash reporting, Monitoring, Session recording, Support, Chat, Payments, Sign-in or Google services. Any other outside domain is shown by its registrable domain, such as sdk-collector.example.

Examples are partly hidden everywhere, in the window, the report and the MCP tool, so you can recognize a value without exposing it:

Data Shown as
Email address a•••@example.net
Phone and card numbers +••••••••••23: every digit but the last two hidden
Location 12.9…, 77.5…: one decimal place
Name A•••
Anything else tok_…56: the first 4 and last 2 characters, or the first 2 for values of 8 characters or fewer

Copy Report puts the map on the clipboard as Markdown, for a privacy review, a ticket or your release checklist:

  • A heading with the app, how many of its requests went to outside companies, and the domains treated as its own.
  • Summary: each kind of data and the companies that received it.
  • A section per company with a table of Data, Where, Example and Requests, including where backend values came from.
  • The companies that received no personal data.
  • Privacy label starting point: the table of data types, App Store categories and companies.

It ends with a note that values are partly hidden and only decrypted HTTPS traffic was checked.

  • Only decrypted traffic. Requests that pass through as encrypted tunnels can’t be read. Turn on SSL Proxying for the app, and capture it through DNS if it ignores the proxy. Apps that pin certificates can’t be decrypted at all.
  • Only what you captured. Data sent on screens you didn’t open, or by SDKs that send in batches later, isn’t on the map. Use the app thoroughly, then wait a moment before you rescan.
  • Only plain HTTP payloads. SDKs that encrypt or encode their payloads before sending them, or send data over something other than HTTP, such as raw sockets, don’t show their contents.
  • Heuristics can miss or misclassify. Data under unusual key names isn’t recognized unless it has a recognizable shape or matches your test account. A value can also be flagged that isn’t personal data. Check findings against the request before you act on them.
  • Your own servers aren’t checked. Data sent to the app’s own domains doesn’t appear, even though it may still belong on a privacy label.

The MCP server has a read-only privacy_map tool. It takes the app name (as list_apps shows it) and optionally first_party, a list of the app’s own registrable domains. Without first_party, it uses the domains you saved for the app in the window, or the suggested ones. It returns each kind of data with its recipients and App Store category, and each company’s findings with partly hidden examples. It doesn’t use the test account values. See the MCP tools reference.