Comparing version 0.5.1 to 1.0.0
@@ -84,2 +84,22 @@ import { Har } from "har-format"; | ||
* by the tracker. | ||
* | ||
* @remarks | ||
* - `state` here means "subnational political entity" | ||
* - Locales should not be listed under `country` | ||
* - We distinguish the following types of IDs (that are personal data under the GDPR): | ||
* | ||
* - An `advertisingId` is a unique identifier assigned to a device by the operating system that is the same across | ||
* apps/websites. In particular, this includes the Google Advertising ID (GAID) and Apple's Identifier for | ||
* Advertisers (IDFA). These can typically be reset by the user. | ||
* - A `developerScopedId` is a unique identifier assigned to a device by the operating system that is specific to a | ||
* certain app developer. Apps from different developers will see different `developerScopedId`s. In particular, | ||
* this includes Apple's Identifier for Vendor (IDFV), Google's App set ID (ASID), and the `ANDROID_ID`. | ||
* - A `sessionId` identifies a single (time-limited) session and is specific to a certain website/app and device. | ||
* - An `installationId` identifies an installation of an app on a device. It specific to that app and device, and reset | ||
* when the app is un- and reinstalled. | ||
* - A `deviceId` identifies a device across apps/websites. | ||
* - A `userId` identifies a user across apps/websites and devices. | ||
* - We use the `otherIdentifiers` data property to denote UUIDs and other identifiers where we don't know how they are | ||
* actually used. This should only be used sparingly and where, despite not knowing the precise function, it is | ||
* obvious (from context or otherwise) that this ID is personal data. | ||
*/ | ||
@@ -121,3 +141,6 @@ export type Property = keyof (typeof translations)['properties']; | ||
* result. | ||
* - `decodeJwt`: Decodes the payload of a JSON Web Token (JWT) string into an object. | ||
* - `ensureArray`: Ensures that the given value is an array. If it is not, it is wrapped in an array. | ||
* - `gunzip`: Unzips a gzip-compressed blob. | ||
* - `split`: Splits a string into an array using the given separator. | ||
* - `getProperty`: Gets a property from an object. The property name is given in the `options.path` option. This is | ||
@@ -128,4 +151,9 @@ * useful for either copying a nested property to a variable, or to extract a nested property from an array when used | ||
export type DecodingStep = ({ | ||
function: 'parseQueryString' | 'parseJson' | 'decodeBase64' | 'decodeUrl' | 'decodeProtobuf' | 'ensureArray' | 'gunzip'; | ||
function: 'parseQueryString' | 'parseJson' | 'decodeBase64' | 'decodeUrl' | 'decodeProtobuf' | 'decodeJwt' | 'ensureArray' | 'gunzip'; | ||
} | { | ||
function: 'split'; | ||
options: { | ||
separator: string; | ||
}; | ||
} | { | ||
function: 'getProperty'; | ||
@@ -148,2 +176,6 @@ options: { | ||
path: JsonPath; | ||
/** An optional filter that stops a discovered value from being considered an instance of the respective property. */ | ||
notIf?: string | RegExp; | ||
/** An optional filter that causes only matching values to be considered instances of the respective property. */ | ||
onlyIf?: string | RegExp; | ||
/** | ||
@@ -178,3 +210,3 @@ * An explanation of how we concluded that this is information is actually the type of data we labelled it as. This | ||
* The endpoint URL in this context is the full URL, including protocol, host, and path, but excluding the query | ||
* string. | ||
* string. It should not have a trailing slash. | ||
*/ | ||
@@ -279,3 +311,3 @@ endpointUrls: (string | RegExp)[]; | ||
* "localIp": ["10.0.0.2", "fd31:4159::a2a1"], | ||
* "idfa": "6a1c1487-a0af-4223-b142-a0f4621d0311" | ||
* "advertisingId": "6a1c1487-a0af-4223-b142-a0f4621d0311" | ||
* } | ||
@@ -311,4 +343,4 @@ * ``` | ||
valuesOnly?: ValuesOnly | undefined; | ||
indicatorValues?: Partial<Record<LiteralUnion<"accelerometerX" | "accelerometerY" | "accelerometerZ" | "appId" | "appName" | "appVersion" | "architecture" | "batteryLevel" | "carrier" | "country" | "deviceName" | "diskFree" | "diskTotal" | "diskUsed" | "hashedIdfa" | "idfa" | "idfv" | "installTime" | "isCharging" | "isEmulator" | "isFirstLaunch" | "isInDarkMode" | "isInForeground" | "isRoaming" | "isRooted" | "language" | "latitude" | "localIp" | "longitude" | "macAddress" | "manufacturer" | "model" | "networkConnectionType" | "orientation" | "osName" | "osVersion" | "otherIdentifiers" | "publicIp" | "pushNotificationToken" | "ramFree" | "ramTotal" | "ramUsed" | "referer" | "revenue" | "rotationX" | "rotationY" | "rotationZ" | "screenHeight" | "screenWidth" | "signalStrengthCellular" | "signalStrengthWifi" | "startTime" | "state" | "timeSpent" | "timezone" | "trackerSdkVersion" | "uptime" | "userAgent" | "viewedPage" | "volume", string>, ArrayOrSingle<string>>> | undefined; | ||
} | undefined) => Promise<ValuesOnly extends true ? (Partial<Record<LiteralUnion<"accelerometerX" | "accelerometerY" | "accelerometerZ" | "appId" | "appName" | "appVersion" | "architecture" | "batteryLevel" | "carrier" | "country" | "deviceName" | "diskFree" | "diskTotal" | "diskUsed" | "hashedIdfa" | "idfa" | "idfv" | "installTime" | "isCharging" | "isEmulator" | "isFirstLaunch" | "isInDarkMode" | "isInForeground" | "isRoaming" | "isRooted" | "language" | "latitude" | "localIp" | "longitude" | "macAddress" | "manufacturer" | "model" | "networkConnectionType" | "orientation" | "osName" | "osVersion" | "otherIdentifiers" | "publicIp" | "pushNotificationToken" | "ramFree" | "ramTotal" | "ramUsed" | "referer" | "revenue" | "rotationX" | "rotationY" | "rotationZ" | "screenHeight" | "screenWidth" | "signalStrengthCellular" | "signalStrengthWifi" | "startTime" | "state" | "timeSpent" | "timezone" | "trackerSdkVersion" | "uptime" | "userAgent" | "viewedPage" | "volume", string>, any[]>> | undefined)[] : (AnnotatedResult | undefined)[]>; | ||
indicatorValues?: Partial<Record<LiteralUnion<"accelerometerX" | "accelerometerY" | "accelerometerZ" | "advertisingId" | "appId" | "appName" | "appVersion" | "architecture" | "batteryLevel" | "browserId" | "browserWindowHeight" | "browserWindowWidth" | "campaignCreative" | "campaignCreativePosition" | "campaignMedium" | "campaignName" | "campaignSource" | "campaignTerm" | "carrier" | "consentState" | "country" | "currency" | "developerScopedId" | "deviceId" | "deviceName" | "diskFree" | "diskTotal" | "diskUsed" | "errorInformation" | "hashedAdvertisingId" | "installationId" | "installTime" | "interactedElement" | "isAutomated" | "isCharging" | "isConversion" | "isDntEnabled" | "isEmulator" | "isFirstLaunch" | "isInDarkMode" | "isInForeground" | "isJsEnabled" | "isMobileDevice" | "isRoaming" | "isRooted" | "isUserActive" | "isUserInactive" | "language" | "lastActivityTime" | "latitude" | "localIp" | "longitude" | "macAddress" | "manufacturer" | "model" | "networkConnectionType" | "orientation" | "osName" | "osVersion" | "otherIdentifiers" | "pageHeight" | "pageWidth" | "propertyId" | "publicIp" | "pushNotificationToken" | "ramFree" | "ramTotal" | "ramUsed" | "referer" | "revenue" | "rotationX" | "rotationY" | "rotationZ" | "screenColorDepth" | "screenHeight" | "screenWidth" | "scrollPositionX" | "scrollPositionY" | "segment" | "sessionCount" | "sessionDuration" | "sessionId" | "signalStrengthCellular" | "signalStrengthWifi" | "startTime" | "state" | "timeSpent" | "timezone" | "trackerSdkVersion" | "uptime" | "userAction" | "userActionSource" | "userActiveTime" | "userAgent" | "userGender" | "userId" | "userInterests" | "viewedPage" | "viewedPageCategory" | "viewedPageKeywords" | "viewedPageLanguage" | "volume" | "websiteName" | "websiteUrl", string>, ArrayOrSingle<string>>> | undefined; | ||
} | undefined) => Promise<ValuesOnly extends true ? (Partial<Record<LiteralUnion<"accelerometerX" | "accelerometerY" | "accelerometerZ" | "advertisingId" | "appId" | "appName" | "appVersion" | "architecture" | "batteryLevel" | "browserId" | "browserWindowHeight" | "browserWindowWidth" | "campaignCreative" | "campaignCreativePosition" | "campaignMedium" | "campaignName" | "campaignSource" | "campaignTerm" | "carrier" | "consentState" | "country" | "currency" | "developerScopedId" | "deviceId" | "deviceName" | "diskFree" | "diskTotal" | "diskUsed" | "errorInformation" | "hashedAdvertisingId" | "installationId" | "installTime" | "interactedElement" | "isAutomated" | "isCharging" | "isConversion" | "isDntEnabled" | "isEmulator" | "isFirstLaunch" | "isInDarkMode" | "isInForeground" | "isJsEnabled" | "isMobileDevice" | "isRoaming" | "isRooted" | "isUserActive" | "isUserInactive" | "language" | "lastActivityTime" | "latitude" | "localIp" | "longitude" | "macAddress" | "manufacturer" | "model" | "networkConnectionType" | "orientation" | "osName" | "osVersion" | "otherIdentifiers" | "pageHeight" | "pageWidth" | "propertyId" | "publicIp" | "pushNotificationToken" | "ramFree" | "ramTotal" | "ramUsed" | "referer" | "revenue" | "rotationX" | "rotationY" | "rotationZ" | "screenColorDepth" | "screenHeight" | "screenWidth" | "scrollPositionX" | "scrollPositionY" | "segment" | "sessionCount" | "sessionDuration" | "sessionId" | "signalStrengthCellular" | "signalStrengthWifi" | "startTime" | "state" | "timeSpent" | "timezone" | "trackerSdkVersion" | "uptime" | "userAction" | "userActionSource" | "userActiveTime" | "userAgent" | "userGender" | "userId" | "userInterests" | "viewedPage" | "viewedPageCategory" | "viewedPageKeywords" | "viewedPageLanguage" | "volume" | "websiteName" | "websiteUrl", string>, any[]>> | undefined)[] : (AnnotatedResult | undefined)[]>; | ||
/** | ||
@@ -315,0 +347,0 @@ * An array of all available adapters. |
@@ -6,2 +6,3 @@ { | ||
"accelerometerZ": "Accelerometer Z", | ||
"advertisingId": "Device advertising ID (GAID/IDFA)", | ||
"appId": "App ID", | ||
@@ -12,4 +13,17 @@ "appName": "App name", | ||
"batteryLevel": "Battery level", | ||
"browserId": "Unique ID for the browser", | ||
"browserWindowHeight": "Browser window height", | ||
"browserWindowWidth": "Browser window width", | ||
"campaignCreative": "Campaign creative/content (utm_content)", | ||
"campaignCreativePosition": "Campaign creative position", | ||
"campaignMedium": "Campaign medium (utm_medium)", | ||
"campaignName": "Campaign name/ID (utm_campaign)", | ||
"campaignSource": "Campaign source (utm_source)", | ||
"campaignTerm": "Campaign term/keyword (utm_term)", | ||
"carrier": "Carrier", | ||
"consentState": "Consent state", | ||
"country": "Country", | ||
"currency": "Currency", | ||
"developerScopedId": "Developer-scoped device ID (IDFV/ASID/ANDROID_ID)", | ||
"deviceId": "Unique device ID", | ||
"deviceName": "Device name", | ||
@@ -19,14 +33,23 @@ "diskFree": "Free disk space", | ||
"diskUsed": "Used disk space", | ||
"hashedIdfa": "Hashed device advertising ID", | ||
"idfa": "Device advertising ID (GAID/IDFA)", | ||
"idfv": "Developer-scoped device ID (IDFV/ASID/ANDROID_ID)", | ||
"errorInformation": "Error information", | ||
"hashedAdvertisingId": "Hashed device advertising ID", | ||
"installationId": "Unique installation ID", | ||
"installTime": "App install time", | ||
"interactedElement": "Element the user interacted with", | ||
"isAutomated": "Is device/browser controlled automatically?", | ||
"isCharging": "Charging status", | ||
"isConversion": "Is this a conversion event?", | ||
"isDntEnabled": "Is “Do Not Track” enabled?", | ||
"isEmulator": "Is device an emulator?", | ||
"isFirstLaunch": "Is first launch?", | ||
"isFirstLaunch": "Is first launch/visit?", | ||
"isInDarkMode": "Is app in dark mode?", | ||
"isInForeground": "Is app in foreground?", | ||
"isJsEnabled": "Is JavaScript enabled?", | ||
"isMobileDevice": "Is device a mobile device?", | ||
"isRoaming": "Is device roaming?", | ||
"isRooted": "Is device rooted?", | ||
"isUserActive": "Is user active?", | ||
"isUserInactive": "Is user inactive?", | ||
"language": "Language", | ||
"lastActivityTime": "Time of last activity by the user", | ||
"latitude": "Latitude", | ||
@@ -42,3 +65,6 @@ "localIp": "Local IP address(es)", | ||
"osVersion": "OS version", | ||
"otherIdentifiers": "Other unique identifiers for the user, device, session, or installation", | ||
"otherIdentifiers": "Other unique identifier", | ||
"pageHeight": "Page height", | ||
"pageWidth": "Page width", | ||
"propertyId": "ID for the property (website/app)", | ||
"publicIp": "Public IP address", | ||
@@ -54,4 +80,11 @@ "pushNotificationToken": "Push notification token", | ||
"rotationZ": "Rotation Z", | ||
"screenColorDepth": "Screen color depth", | ||
"screenHeight": "Screen height", | ||
"screenWidth": "Screen width", | ||
"scrollPositionX": "Scroll position X", | ||
"scrollPositionY": "Scroll position Y", | ||
"segment": "Segment for the user", | ||
"sessionCount": "Session count", | ||
"sessionDuration": "Session duration", | ||
"sessionId": "Unique session ID", | ||
"signalStrengthCellular": "Signal strength (cellular)", | ||
@@ -65,16 +98,39 @@ "signalStrengthWifi": "Signal strength (Wi-Fi)", | ||
"uptime": "Uptime", | ||
"userAction": "Action performed by the user", | ||
"userActionSource": "Source of the action performed by the user", | ||
"userActiveTime": "How long has the user been active?", | ||
"userAgent": "User agent", | ||
"userGender": "Gender of the user", | ||
"userId": "Unique user ID", | ||
"userInterests": "User interests", | ||
"viewedPage": "Viewed page", | ||
"volume": "Volume" | ||
"viewedPageCategory": "Category of the viewed page", | ||
"viewedPageKeywords": "Keywords related to the viewed page", | ||
"viewedPageLanguage": "Language of the viewed page", | ||
"volume": "Volume", | ||
"websiteName": "Website name", | ||
"websiteUrl": "Website URL" | ||
}, | ||
"tracker-descriptions": { | ||
"adjust": "Adjust offers the following services:\n\n- User engagement tracking using events. “You can define in-app events for your app to measure user registrations, add-to-carts, or level ups, while setting up revenue events lets you record in-app purchases and transactions. Set up events to: See where your users go directly after install, Discover the app features your users like the most, Identify the last thing a user does before they become inactive”[https://help.adjust.com/en/article/add-events]\n- Mobile attribution, in order to “[i]dentify [the] best users and channels”.[https://www.adjust.com/product/mobile-app-attribution/] “Adjust's attribution matches your app users to the source that drove their install. You can use this attribution data to measure campaign performance, run effective retargeting campaigns, optimize your creative assets, and more.”[https://help.adjust.com/en/article/attribution-methods] Additionally, “Adjust can reattribute dormant users who engage with a new source and then return to [the] app.”[https://help.adjust.com/en/article/reattribution]\n\n Adjust uses two attribution methods:\n - “Deterministic attribution is Adjust's main attribution method and involves device matching. We collect a unique identifier from recorded engagements and installs, and if both IDs match, we can attribute that engagement to the install. With a 100% accuracy rate, click-based device matching is the most reliable attribution method. We use deterministic attribution to attribute installs (first app opens) and reattribute (assign new attribution sources to) inactive users. Adjust uses the following identifiers for deterministic attribution: Advertising IDs […], Device IDs […], Adjust reftags […]”[https://help.adjust.com/en/article/attribution-methods#deterministic-attribution]\n - “Probabilistic modeling […] uses machine learning to support a statistical approach to measurement.”[https://help.adjust.com/en/article/attribution-methods#probabilistic-modeling]\n- Uninstall and reinstall tracking. “When a user installs [the] app, the app is given a unique push token which the Adjust SDK forwards to Adjust's servers. Silent push notifications are then sent once per day to check if the app is still installed.”[https://help.adjust.com/en/article/uninstalls-reinstalls]\n- Audience segmentation to “group users together based on […] criteria”.[https://www.adjust.com/product/audience-builder/]\n- Fraud prevention. “Organic users are captured accurately and not misattributed”.[https://www.adjust.com/product/fraud-prevention/]\n\nAdditionally, Adjust can pull in tracking data from partner companies.[https://help.adjust.com/en/article/partner-connections]", | ||
"branch-io-attribution-api": "The Branch Attribution API is used for “deep linking and session attribution. […] Every time the API is called, it will track an INSTALL, REINSTALL, or OPEN event in Branch and return deep link data in the response if the session is attributed.”[https://help.branch.io/developers-hub/reference/attribution-api] It can also track “additional downstream conversion events” like PURCHASE.[https://help.branch.io/developers-hub/reference/attribution-api#tracking-downstream-events]", | ||
"branch-io": "Branch offers the following services:\n\n- Mobile attribution[https://www.branch.io/attribution/] to “[c]apture every customer touchpoint across any channel, platform, OS to optimize […] campaigns and maximize ROI.”[https://www.branch.io/features/]\n- Ad conversion tracking. Branch can “[r]etarget app users who see a web ad and then purchase in the app, attribute revenue to the web ad that drove the install, and measure cumulative revenue from users across both web and app.”[https://www.branch.io/universal-ads/]\n- Custom audiences to “communicate the perfect message to the ideal customer, at the right moment”. “Get higher return on ad spend (ROAS) with precision retargeting of high-value active users and eliminate wasted spend in your acquisition campaigns by excluding existing customers. Re-engage lapsed users, boost propensity to purchase, and increase sessions per user.”[https://www.branch.io/engagement-builder/]\n- Fraud protection.[https://www.branch.io/fraud-protection/]\n\nBranch provides integrations to automatically “send Branch data to […] marketing and analytics partners to measure and optimize […] campaigns.”[https://www.branch.io/data-feeds/]", | ||
"branch-io-attribution-api": "The Branch Attribution API is used for “deep linking and session attribution. […] Every time the API is called, it will track an INSTALL, REINSTALL, or OPEN event in Branch and return deep link data in the response if the session is attributed.”[https://help.branch.io/developers-hub/reference/attribution-api] It can also track “additional downstream conversion events” like PURCHASE.[https://help.branch.io/developers-hub/reference/attribution-api#tracking-downstream-events]", | ||
"chartboost": "Chartboost is an advertising platform focused on mobile gaming that caters to both publishers[https://www.chartboost.com/products/monetization/] and advertisers[https://www.chartboost.com/products/advertising/].\n\nChartboost supports mediation (real-time bidding)[https://www.chartboost.com/products/mediation/], analytics[https://docs.chartboost.com/en/mediation/analytics/], and A/B testing[https://docs.chartboost.com/en/mediation/ab-tests/].", | ||
"google-fundingchoices": "With Google's Privacy & Messaging API (formerly Funding Choices[https://support.google.com/fundingchoices/answer/9010669?hl=en]), app developers can manage users' consent choices[https://developers.google.com/funding-choices] and show consent forms[https://developers.google.com/admob/android/privacy#load-and-show-form]. It can also be used to detect ad blockers and display messages to “recover lost revenue from ad blocking users”.[https://support.google.com/admob/answer/10107561]\n\nThe Privacy & Messaging API is available through Google's AdMob, Ad Manager, and AdSense SDKs on the web, Android, and iOS.[https://support.google.com/fundingchoices/answer/9010669?hl=en]", | ||
"criteo-commerce-grid-prebidjs": "Criteo Commerce Grid is a supply-side platform that superseded IPONWEB’s “The MediaGrid SSP” with Criteo’s legacy “Direct Bidder” solution.[https://www.criteo.com/wp-content/uploads/2023/06/Criteo-Launches-First-ever-Supply-side-Platform-Built-for-Commerce.pdf]\n\nIt uses the OpenRTB standard through the Prebid.org header bidding solution.", | ||
"criteo-grid-bidder-prebidjs": "This endpoint uses the OpenRTB standard through the Prebid.js header bidding solution.", | ||
"criteo": "Criteo is an advertising company for online display advertisements.\n\nCriteo boasts with being able to perfectly tailor and personalize ads to each individual user, offering services like:\n\n- Predictive Bidding, which supposedly “accurately forecasts each shopper’s engagement, conversion, and average order value, and bids the right price for each ad inventory to reach them.”[https://www.criteo.com/technology/predictive-bidding/]\n- Product Recommendations, which “consider a shopper’s overall browsing habits, and go beyond what was last viewed or best sellers“[https://www.criteo.com/technology/product-recommendations/]\n- Audience Targeting: “Reengage people who already know you. Connect your customer data, and target groups like lapsed or loyal customers with dynamic ads that help them discover more of your products that they’ll love.”[https://www.criteo.com/solutions/audiences/]\n\nThese services make use of the Criteo Shopper Graph of “[r]eal-time identity data […] [to ensure] accurate cross-device identification from billions of active shoppers using multiple devices to shop”.[https://www.criteo.com/technology/shopper-graph/] This “continuously growing identity graph connects online and offline shopper identifiers […] for a holistic view of each individual’s shopping intent.”[https://www.criteo.com/technology/shopper-graph/] It also “connects shopper engagement events to products, categories and brand identifiers”.[https://www.criteo.com/technology/shopper-graph/]\n\nCriteo then uses “advanced AI algorithms” to analyze data from the Shopper Graph in real time and “[learn] from real shopper behaviors”.[https://www.criteo.com/technology/ai-engine/] The goal is to “assign a value to each customer and change the bid for every ad placement”.[https://www.criteo.com/technology/ai-engine/]\n\nCriteo lets customers “[e]nrich [their] first-party data with commerce data from 18,000+ advertisers and thousands of publishers”.[https://www.criteo.com/platform/commerce-growth/]", | ||
"doubleclick-cookie-matching-pixel": "The DoubleClick Pixel for Cookie Matching is part of Google’s features for real-time bidding (RTB) to exchange user IDs between bidders and Google: “Cookie Matching is a feature that enables [bidders] to match [their] cookie—for example, an ID for a user that browsed [their] website—with a corresponding bidder-specific Google User ID, and construct user lists that can help [them] make more effective bidding choices.”[https://developers.google.com/authorized-buyers/rtb/cookie-guide]\n\nThe goal of cookie matching is to circumvent the browser security features that would otherwise restrict the reading of cookies set by another domain.[https://developers.google.com/authorized-buyers/rtb/cookie-guide#what-is-cookie-matching]\n\nCookie matching uses match tables that record the mapping from a Google User ID to the user ID in the bidder’s system (and/or vice versa). These match tables can be populated and hosted by bidders themselves or by Google, with Google recommending bidders to let Google host the match tables.[https://developers.google.com/authorized-buyers/rtb/cookie-guide#match-tables]\n\nBidders can additionally sort user IDs into user lists.[https://developers.google.com/authorized-buyers/rtb/cookie-guide#user-lists]", | ||
"facebook-audience-network": "Meta Audience Network is a service by Facebook that allows app developers to monetize their apps with ads.[https://developers.facebook.com/docs/audience-network] Facebook offers Audience Network SDKs for Android[https://developers.facebook.com/docs/audience-network/setting-up/platform-setup/android/add-sdk], iOS[https://developers.facebook.com/docs/audience-network/setting-up/platform-setup/ios/add-sdk], and Unity[https://developers.facebook.com/docs/audience-network/setting-up/platform-steup/unity/add-sdk].", | ||
"facebook-graph-app-events": "The Graph API is provided by Facebook to “get data into and out of the Facebook platform”.[https://developers.facebook.com/docs/graph-api/overview] It can be accessed through the Facebook SDKs for Android[https://developers.facebook.com/docs/android/graph] and iOS[https://developers.facebook.com/docs/ios/graph].\n\nThe App Events endpoint allows developers to “track actions that occur in [a] mobile app or web page such as app installs and purchase events” in order to “measure ad performance and build audiences for ad targeting”. The Facebook SDK automatically logs app installs, app sessions, and in-app purchases using this endpoint. Additionally, developers can manually log their own events.[https://developers.facebook.com/docs/marketing-api/app-event-api]", | ||
"firebaseinstallations": "The Firebase Installations service (FIS) provides a unique identifier for each installed instance of a Firebase app, called Firebase installation ID (FID).[https://firebase.google.com/docs/projects/manage-installations] “Many Firebase services depend on the Firebase Installations API in order to identify app installs and/or authenticate client requests to their servers.”[https://console.cloud.google.com/marketplace/product/google/firebaseinstallations.googleapis.com] Other purposes include user segmentation, message delivery, performance monitoring, tracking the number of unique users, or selecting which configuration values to return.[https://firebase.google.com/docs/projects/manage-installations]\n\nFIDs can also be used by Google Analytics for attribution.[https://firebase.google.com/docs/projects/manage-installations]", | ||
"firebaseremoteconfig": "The Firebase Remote Config service lets developers remotely change the functionality and appearance of their apps without having to publish an app update.[https://firebase.google.com/docs/remote-config]\n\nDevelopers can use user segmentation and return different configuration to different users based on app version, language, Google Analytics audience, and imported segment.[https://firebase.google.com/docs/remote-config] They can also “[use] machine learning to continuously tailor individual user experience to optimize for goals like user engagement, ad clicks, and revenue—or any custom event [they] can measure with Google Analytics”.[https://firebase.google.com/docs/remote-config] Finally, they can use A/B testing.[https://firebase.google.com/docs/remote-config]", | ||
"google-analytics": "Google Analytics is an analytics platform that claims to give publishers a “complete understanding of [their] customers across devices and platforms” to “[u]nderstand how [their] customers interact across […] sites and apps, throughout their entire lifecycle.”[https://marketingplatform.google.com/about/analytics/]\n\nGoogle Analytics provides JavaScript libraries, mobile app SDKs, and an open measurement protocol to collect data from website visitors and app users.[https://marketingplatform.google.com/about/analytics/features/] “Every time a user visits a webpage, the tracking code will collect pseudonymous information about how that user interacted with the page.”[https://support.google.com/analytics/answer/12159447] Based on that, Google then provides additional features:\n\n- Real-time reporting, allowing publishers to “[m]onitor activity on [their] site or app as it happens.”[https://marketingplatform.google.com/about/analytics/features/]\n- Attribution, allowing publishers to “[u]nderstand [their] customers’ paths to conversion”.[https://marketingplatform.google.com/about/analytics/features/]\n- Path and user exploration. Publishers can “[v]isualize [their] users’ paths to conversion as they interact with [their] website and app.”[https://marketingplatform.google.com/about/analytics/features/] They can even “[s]elect specific groups of users and drill down deeper to understand how those users engage with [the] site or app.“[https://marketingplatform.google.com/about/analytics/features/]\n- Funnel exploration: “Visualize the steps users take to complete tasks on [the] site or app, […] and identify over- or under-performing audiences.”[https://marketingplatform.google.com/about/analytics/features/]\n- Predictive capabilities: “By applying […] machine learning models, Analytics can analyze [publishers’] data and predict future actions people may take, like making a purchase or churning. [They] can then create audiences that are predicted to take these actions to drive conversions or retain more of [their] users.”[https://marketingplatform.google.com/about/analytics/features/]\n\nGoogle Analytics integrates with various other Google products, including Google Ads, Google Search Ads 360, Google Search Console, and Google Play.[https://marketingplatform.google.com/about/analytics/features/]\n\nThere have been multiple generations of Google Analytics. The current generation is Google Analytics 4 (GA4). The previous generation, Universal Analytics, stopped processing data in July 2023. It used property tags starting with “UA-“.[https://support.google.com/analytics/answer/11583528]", | ||
"google-fundingchoices": "With Google's Privacy & Messaging API (formerly Funding Choices[https://support.google.com/fundingchoices/answer/9010669?hl=en]), app developers can manage users' consent choices[https://developers.google.com/funding-choices] and show consent forms[https://developers.google.com/admob/android/privacy#load-and-show-form]. It can also be used to detect ad blockers and display messages to “recover lost revenue from ad blocking users”.[https://support.google.com/admob/answer/10107561]\n\nThe Privacy & Messaging API is available through Google's AdMob, Ad Manager, and AdSense SDKs on the web, Android, and iOS.[https://support.google.com/fundingchoices/answer/9010669?hl=en]", | ||
"google-gtag": "Google tag (gtag.js) is a single script that publishers can add to their website to include and use multiple Google measurement and advertising products like Google Ads, Google Analytics, Campaign Manager, Display & Video 360, and Search Ads 360.[https://developers.google.com/tag-platform/gtagjs]\n\nGoogle advertises that the Google tag can be used for example to “[a]utomatically measure page views, clicks, scrolls, and more in Google Analytics” and “[a]utomatically measure key events and campaign performance in Google Ads”.[https://support.google.com/tagmanager/answer/7582054]", | ||
"google-publisher-tag": "The Google Publisher Tag (GPT) is an ad tagging library for Google Ad Manager.[https://developers.google.com/publisher-tag/guides/get-started] By default, Google serves personalized ads through GPT, “with ad selection based on both the content of the web page and the history of the individual user visiting the page.”[https://support.google.com/admanager/answer/7678538]", | ||
"googledatatransport-batchlog": "The GoogleDataTransport SDK is a transport layer used internally by many other Firebase (e.g. Crashlytics, Performance, Core) Google (e.g. ML Kit) SDKs and services.[https://github.com/firebase/firebase-ios-sdk/issues/8220#issuecomment-858040701] It batches application-specific data from within an app to Google, using a common endpoint regardless of the actual SDK that was integrated by the app developer.[https://stackoverflow.com/a/76334853]", | ||
"googletagmanager-gtm": "Google Tag Manager is a tag management system that allows publishers to add and update tags for analytics, marketing, and advertising services without modifying the website/app code directly.[https://support.google.com/tagmanager/answer/6102821]\n\nIn addition to Google's own tags like Google Ads and Google Analytics, it also supports many third-party tags like Adobe Analytics, AppsFlyer, Criteo, and Microsoft Bing Ads as well as custom tags.[https://support.google.com/tagmanager/answer/6106924]", | ||
"id5": "The ID5 ID is a shared identifier to be used by publishers, advertisers and ad tech platform, designed for scenarios where 3rd party cookies may not be available.[https://docs.prebid.org/dev-docs/modules/userid-submodules/id5.html] It claims to be able to recognize users across different types of devices and relies on signals like hashed email addresses, page URL, IP addresses, timestamps, as well as a machine learning algorithm.[https://github.com/id5io/id5-api.js/blob/874ace5d11a667b992650df198d53775fdb60709/README.md#id5-id] The explicit goal is to have a single user identifier that is shared across the entire ecosystem.[https://wiki.id5.io/en/identitycloud/retrieve-id5-ids/prebid-user-id-module/id5-prebid-user-id-module]\n\nID5 requires publishers and advertisers to send data like IP address and user agent, and recommends sharing data like hashed email addresses and mobile advertising IDs.[https://wiki.id5.io/identitycloud/retrieve-id5-ids/passing-partner-data-to-id5#what-is-partner-data]", | ||
"id5-cookie-sync": "The ID5 Cookie Sync Pixel is used to synchronize and share the ID5 ID with the user IDs of other ad tech vendors.[https://wiki.id5.io/en/identitycloud/cookie-sync-with-id5/inititiate-cookie-sync-to-id5]", | ||
"id5-prebid-user-id": "This endpoint is used to integrate ID5 into the User ID module of the Prebid.js header bidding solution.[https://docs.prebid.org/dev-docs/modules/userid-submodules/id5.html] With this integration, demand partners configured in Prebid can retrieve the ID5 ID and share it with server-side RTB partners, allowing them to target individual users.[https://wiki.id5.io/en/identitycloud/retrieve-id5-ids/prebid-user-id-module/id5-prebid-user-id-module#how-does-the-id5-id-work]", | ||
"infonline": "INFOnline provides digital audience measurement for websites and apps.[https://www.infonline.de/en/]\n\nThey offer two separate measurement systems: Census Measurement (IOMb[https://www.infonline.de/download/?wpdmdl=7135]) and INFOnline Measurement pseudonymous (IOMp[https://www.infonline.de/download/?wpdmdl=7135], formerly SZMnG[https://www.infonline.de/faqs/]). Census Measurement can be recognized by the URL path fragment “base.io”, whereas INFOnline Measurement pseudonymous uses “tx.io”.[https://docs.infonline.de/infonline-measurement/en/integration/web/checkliste_web_allgemein/]\n\nINFOnline boasts with constantly adapting their technology in order to circumvent browser restrictions, ad and tracking blockers, and privacy-preserving changes by operating systems.[https://www.infonline.de/measurement/]", | ||
@@ -85,9 +141,9 @@ "infonline-pseudonymous": "Unlike Census Measurement, which works anonymously without identifiers, INFOnline Measurement pseudonymous “is designed as a pseudonymous system (with client identifiers)”.[https://docs.infonline.de/infonline-measurement/en/getting-started/verfahrensbeschreibung/]\n\nAccording INFOnline’s own documentation, “[…] the pseudonymous INFOnline Measurement may only be loaded and executed if there is active consent from the user of [the] web page. […] The following also applies to apps: Only start the session of pseudonymous measurement if you have the user's consent.”[https://docs.infonline.de/infonline-measurement/en/getting-started/rechtliche-auswirkungen/]", | ||
"mopub": "MoPub was a mobile app monetization service.[https://web.archive.org/web/20210126085400/https://www.mopub.com/en]\n\nMoPub has since been acquired by AppLovin and integrated into AppLovin MAX.[https://www.applovin.com/blog/applovins-acquisition-of-mopub-has-officially-closed/]", | ||
"onesignal-add-a-device": "The “Add a device” endpoint is used ”to register a new device with OneSignal.“[https://documentation.onesignal.com/v9.0/reference/add-a-device]", | ||
"onesignal": "OneSignal provides SDKs and APIs to help developers “send push notifications, in-app messages, SMS, and emails.”[https://documentation.onesignal.com/docs/onesignal-platform]\n\nFor that, it also offers personalization[https://onesignal.com/personalization], user segmentation[https://onesignal.com/targeting-segmentation], and A/B testing[https://documentation.onesignal.com/docs/ab-testing] features. Developers can “send personalized messages based on real-time user behavior, […] user attributes, events, location, language, and more”.[https://onesignal.com/personalization] Audience cohorts can be synced from various analytics providers.[https://onesignal.com/targeting-segmentation]\n\nAdditionally, OneSignal offers analytics features. Developers can “[c]reate an understanding as to how [their] messaging drives direct, indirect, and unattributed user actions” and “[e]asily quantify which messages are driving sales, engagement, and more”.[https://onesignal.com/analytics] OneSignal advertises with helping developers “know precisely when a device receives a notification”, even if “[u]sers uninstall apps, swap phones, turn off their phones, or are unreachable”.[https://onesignal.com/analytics] Analytics data can be shared with various third-party tracking companies using integrations.[https://onesignal.com/integrations/category/analytics]", | ||
"onesignal-add-a-device": "The “Add a device” endpoint is used ”to register a new device with OneSignal.“[https://documentation.onesignal.com/v9.0/reference/add-a-device]", | ||
"smartbear-bugsnag": "BugSnag offers the following services:\n\n- Error monitoring, collecting and visualizing crash data.[https://www.bugsnag.com/error-monitoring/]\n- Real user monitoring, to “[o]ptimize your application based on real-time user actions with your application” and give “visibility into critical performance metrics like hot and cold app starts, network requests, screen-load time and more.”[https://www.bugsnag.com/real-user-monitoring/]", | ||
"singular-net": "Singular offers the following services:\n\n- Analytics on a company's marketing spending and efficacy[https://www.singular.net/marketing-analytics/], with the goal of “[acquiring] the highest value users”[https://www.singular.net/ad-monetization/].\n- Tracking and attribution of users, “connecting the install of a mobile app and the user’s activities inside the app to the marketing campaign that led to the installation”.[https://www.singular.net/mobile-attribution/] “For every install, Singular scans its database for relevant ad interactions (ad clicks and ad views) that originated from the same device […]. […] The goal is to reconstruct the user journey as the first step toward finding the touchpoint that most likely led the user to install the app.”[https://support.singular.net/hc/en-us/articles/115000526963-Understanding-Singular-Mobile-App-Attribution] Additionally, Singular attributes the following events to the user: ”User sessions (i.e., every time the user opens the app), Revenue events (purchases made through the app), Any other events that are relevant to [the] app, such as sign-ups, tutorial completions, or level-ups.“, as well as app uninstalls[https://support.singular.net/hc/en-us/articles/115000526963-Understanding-Singular-Mobile-App-Attribution]\n- Mobile ad fraud prevention.[https://www.singular.net/fraud-prevention/]\n\nSingular boasts with being able to track users across devices, using “advertiser-reported IDs to tie different devices to the same user”.[https://www.singular.net/cross-device-attribution/] They claim: “By implementing an API call to the Singular SDK or server with a user ID, Singular helps you sync up users and devices in such a way that you can recognize customers or users and properly attribute conversions to ad spend and marketing activity, plus assign customers or users to cohorts, regardless of which device or platform they’re using at any given moment.”[https://www.singular.net/glossary/cross-device-attribution/]\n\nAdditionally, they pull in, aggregate, standardize, and match tracking data from thousands of partner companies in the fields of analytics, attribution, audience measurement, and ad monetization.[https://www.singular.net/partner-integrations/]", | ||
"smartbear-bugsnag-notify": "The Error Reporting API is used to send error reports and crashes to BugSnag.[https://bugsnagerrorreportingapi.docs.apiary.io/#reference/0/minidump/send-error-reports]", | ||
"smartbear-bugsnag-session": "The Session Tracking API is used to “notify Bugsnag of sessions starting in web, mobile or desktop applications.”[https://bugsnagsessiontrackingapi.docs.apiary.io/#reference/0/session/report-a-session-starting]", | ||
"smartbear-bugsnag-notify": "The Error Reporting API is used to send error reports and crashes to BugSnag.[https://bugsnagerrorreportingapi.docs.apiary.io/#reference/0/minidump/send-error-reports]", | ||
"singular-net": "Singular offers the following services:\n\n- Analytics on a company's marketing spending and efficacy[https://www.singular.net/marketing-analytics/], with the goal of “[acquiring] the highest value users”[https://www.singular.net/ad-monetization/].\n- Tracking and attribution of users, “connecting the install of a mobile app and the user’s activities inside the app to the marketing campaign that led to the installation”.[https://www.singular.net/mobile-attribution/] “For every install, Singular scans its database for relevant ad interactions (ad clicks and ad views) that originated from the same device […]. […] The goal is to reconstruct the user journey as the first step toward finding the touchpoint that most likely led the user to install the app.”[https://support.singular.net/hc/en-us/articles/115000526963-Understanding-Singular-Mobile-App-Attribution] Additionally, Singular attributes the following events to the user: ”User sessions (i.e., every time the user opens the app), Revenue events (purchases made through the app), Any other events that are relevant to [the] app, such as sign-ups, tutorial completions, or level-ups.“, as well as app uninstalls[https://support.singular.net/hc/en-us/articles/115000526963-Understanding-Singular-Mobile-App-Attribution]\n- Mobile ad fraud prevention.[https://www.singular.net/fraud-prevention/]\n\nSingular boasts with being able to track users across devices, using “advertiser-reported IDs to tie different devices to the same user”.[https://www.singular.net/cross-device-attribution/] They claim: “By implementing an API call to the Singular SDK or server with a user ID, Singular helps you sync up users and devices in such a way that you can recognize customers or users and properly attribute conversions to ad spend and marketing activity, plus assign customers or users to cohorts, regardless of which device or platform they’re using at any given moment.”[https://www.singular.net/glossary/cross-device-attribution/]\n\nAdditionally, they pull in, aggregate, standardize, and match tracking data from thousands of partner companies in the fields of analytics, attribution, audience measurement, and ad monetization.[https://www.singular.net/partner-integrations/]" | ||
"smartbear-bugsnag": "BugSnag offers the following services:\n\n- Error monitoring, collecting and visualizing crash data.[https://www.bugsnag.com/error-monitoring/]\n- Real user monitoring, to “[o]ptimize your application based on real-time user actions with your application” and give “visibility into critical performance metrics like hot and cold app starts, network requests, screen-load time and more.”[https://www.bugsnag.com/real-user-monitoring/]" | ||
} | ||
} |
{ | ||
"name": "trackhar", | ||
"version": "0.5.1", | ||
"version": "1.0.0", | ||
"description": "Library for detecting tracking data transmissions from traffic in HAR format.", | ||
@@ -65,3 +65,4 @@ "bugs": "https://github.com/tweaselORG/TrackHAR/issues", | ||
"fflate": "^0.8.0", | ||
"jsonpath-plus": "^7.2.0", | ||
"jsonpath-plus": "^10.0.0", | ||
"jwt-decode": "^4.0.0", | ||
"protobufjs": "^7.2.3", | ||
@@ -68,0 +69,0 @@ "qs": "^6.11.1" |
@@ -5,3 +5,3 @@ # TrackHAR | ||
For research into mobile privacy and complaints against tracking, it is important to know what data is being transmitted in a request to a tracking server. But these requests are in a huge variety of different formats and often heavily nested and/or obfuscated, which hinders straightforward automatic analysis. TrackHAR aims to address this problem. It takes recorded traffic in a [HAR files](http://www.softwareishard.com/blog/har-12-spec/) as the input and returns a parsed list of the transmitted data (and, optionally, additional metadata like the tracking company and location in the data) for each request it can handle. | ||
For research into privacy in websites and apps, and complaints against tracking, it is important to know what data is being transmitted in a request to a tracking server. But these requests are in a huge variety of different formats and often heavily nested and/or obfuscated, which hinders straightforward automatic analysis. TrackHAR aims to address this problem. It takes recorded traffic in a [HAR files](http://www.softwareishard.com/blog/har-12-spec/) as the input and returns a parsed list of the transmitted data (and, optionally, additional metadata like the tracking company and location in the data) for each request it can handle. | ||
@@ -11,3 +11,3 @@ To achieve this, TrackHAR uses two complementary approaches: adapter-based parsing and indicator matching. | ||
* **Adapter-based parsing**: Our main approach is to use adapters written for specific tracking endpoints. In our [research](https://benjamin-altpeter.de/doc/thesis-consent-dialogs.pdf), we have found that generic approaches (like indicator matching in the raw transmitted plain text or [base64-encoded](https://github.com/baltpeter/base64-search) request content) are not sufficient due to the frankly ridiculous nesting and obfuscation we observed. In addition, approaches that search for static honey data values can never capture dynamic data types such as free disk space and current RAM usage, or low-entropy values like the operating system version (e.g. `11`). | ||
However, we have also noticed that there is a comparatively small number of tracking endpoints which make up a large portion of all app traffic. This makes our adapter-based approach feasible to detect most of the transmitted tracking data. | ||
However, we have also noticed that there is a comparatively small number of tracking endpoints which make up a large portion of all web or app traffic. This makes our adapter-based approach feasible to detect most of the transmitted tracking data. | ||
@@ -57,3 +57,3 @@ * **Indicator matching**: But it will never be possible to write an adapter for every request. Thus, we use indicator matching as a fallback for requests not covered by any adapter. Indicator matching relies on the user providing known honey data values (such as the advertising ID or geolocation) that are then searched for in the requests. TrackHAR supports indicator matching for plain text, base64-encoded and URL-encoded values in the request headers, path, or body. It also tries to match case-insensitively where possible. | ||
adapter: 'yandex/appmetrica', | ||
property: 'otherIdentifiers', | ||
property: 'deviceId', | ||
context: 'query', | ||
@@ -66,3 +66,3 @@ path: 'deviceid', | ||
adapter: 'yandex/appmetrica', | ||
property: 'otherIdentifiers', | ||
property: 'deviceId', | ||
context: 'query', | ||
@@ -114,3 +114,3 @@ path: 'android_id', | ||
{ | ||
otherIdentifiers: [ 'cc89d0f3866e62c804a5a6f81f4aad3b', '355d2c7e339c6855' ], | ||
deviceId: [ 'cc89d0f3866e62c804a5a6f81f4aad3b', '355d2c7e339c6855' ], | ||
osName: [ 'android' ], | ||
@@ -132,3 +132,3 @@ osVersion: [ '13' ] | ||
localIp: [ '10.0.0.2', 'fd31:4159::a2a1' ], | ||
idfa: '6a1c1487-a0af-4223-b142-a0f4621d0311' | ||
advertisingId: '6a1c1487-a0af-4223-b142-a0f4621d0311' | ||
}; | ||
@@ -147,3 +147,3 @@ | ||
adapter: 'indicators', | ||
property: 'idfa', | ||
property: 'advertisingId', | ||
context: 'body', | ||
@@ -228,4 +228,6 @@ path: '$[12]', | ||
In the `Tracker`'s `endpointUrls`, TrackHAR expects an array of strings or regular expressions of all URLs the adapter defines decoding steps and data paths for. Often, you'd want to use a regex to match URLs which might contain some data in the URL as well. TrackHAR always matches against the full URL, including protocol and query. If the requests to two endpoints are similar but slightly different, write two different adapters for them. You should again pull out parts of the adapter into variables to avoid duplicating the code. | ||
In the `Tracker`'s `endpointUrls`, TrackHAR expects an array of strings or regular expressions of all URLs the adapter defines decoding steps and data paths for. Often, you'd want to use a regex to match URLs which might contain some data in the URL as well. TrackHAR always matches against the full URL, including protocol and query. Trailing slashes in requests URLs are ignored and should not be included in the `endpointUrls`. | ||
If the requests to two endpoints are similar but slightly different, write two different adapters for them. You should again pull out parts of the adapter into variables to avoid duplicating the code. | ||
If there are different requests which require specific handling to the same endpoint, you also need to split your adapter to match only a single type of request. To do that, match the adapter to the same endpoint and define a `match` method in both adapters. It receives a `Request` object containing the raw data of the request and should return `true` if the adapter applies to the request. Typically you’d match against characteristic characters in the body or the `Content-Type` header to determine if you an adapter can parse the request, see e.g. this `match` method of a Facebook adapter: | ||
@@ -239,3 +241,3 @@ | ||
If your adapter matches, next it tries to decode the data in the request. Therefore, you must define the algorithm to use in order to decode all relevant data in the `decodingSteps` property of your adapter. Because we automatically generate documentation for the adapters, we defined our own schema describing the decoding algorithm: The `decodingSteps` are an array of `DecodingStep` objects, which contain a function and what parts of the request they should work on. The `function` is set as the string name of a predefined decoding function, such as `parseJson` (look at the [API docs](/docs/README.md) for the full set). Each function takes an `input` argument, which expects a path in the global decoding state. This state is basically an object in which you can write temporary data to any property. It is initialized with the data from each context of the request, so the `body` property contains the raw request body, the `query` property contains the raw query string and so on. You can overwrite those values and, if the values are objects, use a JSONPath, like `body.identifiers.idfa`, to access or overwrite its properties. If you want to run a function on each element of an array, you can also use the `mapInput` property of a `DecodingStep` instead of the `input` property. In that case the function will be mapped over the non-empty entries of the array at the input path, like so: `mapInput.filter((i) => i !== undefined && i !== null).map(function)`. Each `DecodingStep` also expects an `output` property, which specifies the variable in the decoding state in which to return the result of the function to. This can simply be a generic variable name, a or a property access with the `.` operator (notably, here we don’t support all the other features of JSON path), if you want to save the value into a property on an object in the state, and, notably, a path on the special `res` object. The `res` object has one property for each context (`body`, `query`, etc.) and this is the object in which TrackHAR expects the final decoded request, which is then passed on to the next processing step. These are the basics of how to construct the decoding algorithm. Let’s take a look at an example from the Facebook graph adapter: | ||
If your adapter matches, next it tries to decode the data in the request. Therefore, you must define the algorithm to use in order to decode all relevant data in the `decodingSteps` property of your adapter. Because we automatically generate documentation for the adapters, we defined our own schema describing the decoding algorithm: The `decodingSteps` are an array of `DecodingStep` objects, which contain a function and what parts of the request they should work on. The `function` is set as the string name of a predefined decoding function, such as `parseJson` (look at the [API docs](/docs/README.md) for the full set). Each function takes an `input` argument, which expects a path in the global decoding state. This state is basically an object in which you can write temporary data to any property. It is initialized with the data from each context of the request, so the `body` property contains the raw request body, the `query` property contains the raw query string and so on. You can overwrite those values and, if the values are objects, use a JSONPath, like `body.identifiers.advertisingId`, to access or overwrite its properties. If you want to run a function on each element of an array, you can also use the `mapInput` property of a `DecodingStep` instead of the `input` property. In that case the function will be mapped over the non-empty entries of the array at the input path, like so: `mapInput.filter((i) => i !== undefined && i !== null).map(function)`. Each `DecodingStep` also expects an `output` property, which specifies the variable in the decoding state in which to return the result of the function to. This can simply be a generic variable name, a or a property access with the `.` operator (notably, here we don’t support all the other features of JSON path), if you want to save the value into a property on an object in the state, and, notably, a path on the special `res` object. The `res` object has one property for each context (`body`, `query`, etc.) and this is the object in which TrackHAR expects the final decoded request, which is then passed on to the next processing step. These are the basics of how to construct the decoding algorithm. Let’s take a look at an example from the Facebook graph adapter: | ||
@@ -264,2 +266,4 @@ ```js | ||
Sometimes, trackers will transmit something like `idfa: "not found"`. This should obviously not be matched as an advertising ID transmission. To solve that you can use the optional `notIf` or `onlyIf` filters, which, as the names imply, stop a discovered value from being considered an instance of the respective property or cause only matching values to be considered instances of the respective property, respectively. Some values that are clearly always empty (e.g. `unknown`, `none`, `00000000-0000-0000-0000-000000000000`) are also filtered out automatically. | ||
Properties in `containedDataPaths` may also contain an array of several `DataPath`s, because one data type might be found in several places in a request, e.g. the `language` might be part of the `query` but also a property in the `body`. If a property in a request contains more than one data type, it should be mentioned in all of these data types. For example, a property `body.platform` might contain a value like `Android 13.2`, which contains the `osName` as well as the `osVersion`. In this case, you’ll need to add the path `body.platform` to both of these properties in the `containedDataPaths`. | ||
@@ -266,0 +270,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1158559
9
13158
2
281
10
+ Addedjwt-decode@^4.0.0
+ Added@jsep-plugin/assignment@1.3.0(transitive)
+ Added@jsep-plugin/regex@1.0.4(transitive)
+ Addedjsep@1.4.0(transitive)
+ Addedjsonpath-plus@10.3.0(transitive)
+ Addedjwt-decode@4.0.0(transitive)
- Removedjsonpath-plus@7.2.0(transitive)
Updatedjsonpath-plus@^10.0.0