npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

reporthar

v0.1.0

Published

Library for generating technical reports, notices, and GDPR complaints concerning tracking by mobile apps for the tweasel project.

Downloads

7

Readme

ReportHAR

Generate technical reports, controller notices and GDPR complaints concerning tracking by mobile apps for the tweasel project.

ReportHAR is the library for generating the following documents for the tweasel project based on network traffic recordings in HAR files:

  • Technical reports detailing the findings and methodology of our automated analyses concerning tracking and similar data transmissions performed on mobile apps. The reports include a reproduction of the recorded network traffic.
  • Notices to controllers making them aware of data protection violations discovered in their app and giving them the opportunity to remedy the violations.
  • Complaints to data protection authorities about apps that continue to violate data protection law even after the controller was notified and given time to fix the issues. The complaint contains both a technical assessment, based on the traffic analysis, and a detailed legal assessment.

[!NOTE] Currently, ReportHAR only works with templates that are quite specific and hardcoded to our use case with tweasel. Support for custom templates is planned.

All documents are generated as PDF files using Typst. The templates are translatable.

Using ReportHAR is most convenient for traffic recordings made with tweasel tools, which contain additional metadata about the analysis. This way, you don't need to manually provide information about the app, device, etc. However, ReportHAR can also work with HAR files produced by other tools.

ReportHAR doesn't actually analyze the traffic itself, it just produces the documents. You need to use TrackHAR to detect the transmitted personal data and provide that result to ReportHAR.

Installation

You can install ReportHAR using yarn or npm:

yarn add reporthar
# or `npm i reporthar`

API reference

A full API reference can be found in the docs folder.

Example usage

ReportHAR provides two main functions for generating documents: generate() and generateAdvanced().

Usage with tweasel HAR files

generate() is the high-level function that is easiest to use. It expects a tweasel HAR file with additional metadata and automatically extracts all required information from it.

First, we generate the initial technical report and notice to send to the controller:

import { writeFile } from 'fs/promises';
import { process } from 'trackhar';
import { generate } from 'reporthar';

(async () => {
    // We start by loading the HAR file of the initial analysis…
    const initialHar = /* […] */;

    // …and detect the transmitted tracking data using TrackHAR.
    const initialTrackHarResult = await process(initialHar);

    // Then, we pass both to the `generate()` function to generate
    // the technical report…
    const initialReport = await generate({
        type: 'report',
        language: 'en',

        har: initialHar,
        trackHarResult: initialTrackHarResult,
    });
    // …and the controller notice.
    const notice = await generate({
        type: 'notice',
        language: 'en',

        har: initialHar,
        trackHarResult: initialTrackHarResult,
    });

    // This will give you two PDFs that you can for example
    // save to disk.
    await writeFile('initial-report.pdf', initialReport);
    await writeFile('notice.pdf', notice);

    // Remember to store the TrackHAR result as it will also be
    // needed for the complaint.
    await writeFile(
        'initial-trackhar-result.json',
        JSON.stringify(initialTrackHarResult)
    );
})();

If the controller did not appropriately remedy the violations after the deadline, we will send a complaint to the DPAs:

import { writeFile } from 'fs/promises';
import { process } from 'trackhar';
import { generate, parseNetworkActivityReport } from 'reporthar';

(async () => {
    // We again start by loading the HAR files of the initial and
    // second analysis, as well as the TrackHAR analysis of the
    // initial analysis that we have stored previously.
    const initialHar = /* […] */;
    const secondHar = /* […] */;

    const initialTrackHarResult = /* […] */;
    
    // Again, we detect the transmitted tracking data in the
    // second HAR using TrackHAR.
    const secondTrackHarResult = await process(secondHar);

    // Based on that, we generate the second report.
    const secondReport = await generate({
        type: 'report',
        language: 'en',

        har: secondHar,
        trackHarResult: secondTrackHarResult,
    });

        
    // For the complaint, we also load and parse a report of the
    // network activity on the user's device created using the iOS
    // App Privacy Report or the Tracker Control app on Android.
    // This is to prove that the user making the complaint was
    // personally affected by the tracking.
    const userNetworkActivityRaw = /* […] */;
    const userNetworkActivity = parseNetworkActivityReport(
        'tracker-control-csv',
        userNetworkActivityRaw
    );

    // We can then also generate the complaint, providing a whole
    // bunch of additional metadata.
    const complaint = await generate({
        type: 'complaint',
        language: 'en',

        initialHar: initialHar,
        initialTrackHarResult,
        har: secondHar,
        trackHarResult: secondTrackHarResult,

        complaintOptions: {
            date: new Date(),
            reference: '2024-1ONO079C',
            noticeDate: new Date('2023-12-01'),

            nationalEPrivacyLaw: 'TTDSG',

            complainantAddress: 'Kim Mustermensch, Musterstraße 123, 12345 Musterstadt, Musterland',
            controllerAddress: 'Musterfirma, Musterstraße 123, 12345 Musterstadt, Musterland',
            controllerAddressSourceUrl: 'https://play.google.com/store/apps/details?id=tld.sample.app',

            userDeviceAppStore: 'Google Play Store',
            loggedIntoAppStore: true,
            deviceHasRegisteredSimCard: true,

            controllerResponse: 'denial',

            complainantContactDetails: '[email protected]',
            complainantAgreesToUnencryptedCommunication: true,

            userNetworkActivity,
        },
    });

    await writeFile('second-report.pdf', secondReport);
    await writeFile('complaint.pdf', complaint);
})();

Usage with regular HAR files

If you want to use ReportHAR with HAR files from other sources that don't include the tweasel metadata, you need to use the generateAdvanced() function instead and manually specify the information about the app and analysis that would otherwise be included in the HAR file.

Otherwise, the flow is the same as above.

const initialAnalysis = {
    date: new Date('2023-12-01T10:00:00.000Z'),

    deviceType: 'emulator',
    platformVersion: '13',
    platformBuildString: 'lineage_ocean-userdebug 13 TQ2A.230505.002 8c3345902f',
    deviceManufacturer: 'motorola',
    deviceModel: 'moto g(7) power',

    har: initialHar,
    harMd5: '1ee2afb03562aa4d22352ed6b2548a6b',

    trackHarResult: initialTrackHarResult,

    app: {
        platform: 'Android',

        id: 'tld.sample.app',
        name: 'Sample App',
        version: '1.2.3',
        url: 'https://play.google.com/store/apps/details?id=tld.sample.app',
        store: 'Google Play Store',
    },

    dependencies: {
        "python": "3.11.3",
        "mitmproxy": "9.0.1"
    },
};

const initialReport = await generateAdvanced({
    type: 'report',
    language: 'en',

    analysis: initialAnalysis,
});
const notice = await generateAdvanced({
    type: 'notice',
    language: 'en',

    analysis: initialAnalysis,
});

const secondTrackHarResult = await process(secondHar);
const secondAnalysis = {
    date: new Date('2024-02-01T10:00:00.000Z'),

    deviceType: 'emulator',
    platformVersion: '13',
    platformBuildString: 'lineage_ocean-userdebug 13 TQ2A.230505.002 8c3345902f',
    deviceManufacturer: 'motorola',
    deviceModel: 'moto g(7) power',

    har: secondHar,
    harMd5: '2bb3aec14673bb5e33463fe7c3658b7d',

    trackHarResult: secondTrackHarResult,

    app: {
        platform: 'Android',

        id: 'tld.sample.app',
        name: 'Sample App',
        version: '1.2.4',
        url: 'https://play.google.com/store/apps/details?id=tld.sample.app',
        store: 'Google Play Store',
    },

    dependencies: {
        "python": "3.11.3",
        "mitmproxy": "9.0.1"
    },
};

const secondReport = await generateAdvanced({
    type: 'report',
    language: 'en',

    analysis: secondAnalysis,
});
const complaint = await generateAdvanced({
    type: 'complaint',
    language: 'en',

    initialAnalysis,
    analysis: secondAnalysis,

    complaintOptions: {
        date: new Date('2024-02-15'),
        reference: '2024-1ONO079C',
        noticeDate: new Date('2023-12-01'),

        nationalEPrivacyLaw: 'TTDSG',

        complainantAddress: 'Kim Mustermensch, Musterstraße 123, 12345 Musterstadt, Musterland',
        controllerAddress: 'Musterfirma, Musterstraße 123, 12345 Musterstadt, Musterland',
        controllerAddressSourceUrl: 'https://play.google.com/store/apps/details?id=tld.sample.app',

        userDeviceAppStore: 'Google Play Store',
        loggedIntoAppStore: true,
        deviceHasRegisteredSimCard: true,

        controllerResponse: 'denial',

        complainantContactDetails: '[email protected]',
        complainantAgreesToUnencryptedCommunication: true,

        userNetworkActivity,
    },
});

License

This code is licensed under the MIT license, see the LICENSE file for details.

Issues and pull requests are welcome! Please be aware that by contributing, you agree for your work to be licensed under an MIT license.