TutorialsAug 29, 202513 min read

Automating GDPR compliance for web applications with CircleCI

Marta Palandri

Senior Editor of Engineering Content

Since 2018, the General Data Protection Regulation (GDPR) compliance has been an important milestone in the evolution of privacy laws for web application users across Europe. GDPR requires companies to obtain explicit user consent for data collection and processing, and only for specified, legitimate purposes. It’s a law based on principles of transparency and purpose limitation. This law applies to global companies dealing with EU citizen data, giving individuals control over personal data.

When it comes to web applications, GDPR sets strict requirements:

  • User consent: Consent must be informed, given freely, and it should be possible to withdraw at any time without obstacles. Opt-in to data collection should be explicit.

  • Data security and encryption: Applications should implement strong encryption to protect data both at rest and in transit.

  • Right to access and data portability: Users should always be able to ask for a copy of their personal data in a structured format (like JSON or CSV).

  • Data deletion requests: Users should always be allowed to request the deletion of their personal data, and web applications are obliged to provide mechanisms to remove user data from databases and backups.

  • Minimization and purpose limitation: Only necessary data should be collected, and it should only be used for the stated purpose.

  • Audit logging and compliance reports: Compliance reports have to be generated to prove adherence to GDPR guidelines, and applications are obliged to track access to personal data for auditing purposes.

As GDPR symbolizes an important step towards a fairer use of sensitive data, companies and developers have had to find new ways to work around and with these regulations to design and deliver software that maintains compliance and avoid incurring into hefty fines. Navigating these policies is important, but not necessarily straightforward. This tutorial’s focus is to guide developers through the intricacies of GDPR-compliant web app development.

By the end of this tutorial, you will:

  • Understand the fundamental principles of GDPR compliance, including its implications for web applications.

  • Learn how to set up and configure a CircleCI pipeline to automate GDPR-related tasks such as security testing, privacy audits, compliance reporting, and automated tests for data privacy, user consent, and overall GDPR compliance.

Prerequisites

In order to follow this tutorial successfully, it is best to have a basic understanding of web application development and version control. You don’t need to have a deep knowledge of CI/CD or GDPR, as everything you need to know will be covered. However, it is good to have familiarity with:

  • Python:: You will create a simple GDPR compliance script using Python.

  • Snyk: for dependency vulnerability scanning.

  • Git: Knowledge of version control will be fundamental in this tutorial; you will use Git to manage code repositories. Visit the downloads page and have it installed in your machine.

  • Basic command line skills: You should be comfortable running simple commands in a terminal to set up and test applications.

You will also need the following in your toolkit:

  • A CircleCI account: Necessary to configure and automate the CI/CD pipeline. You can create a free account here.

  • A GitHub account: If you don’t have a GitHub account, create one here.

With these prerequisites in place, you’ll be ready to set up a GDPR-compliant CI/CD pipeline.

GDPR compliance app waltkthrough

Now that you have all the tools lined up, you can create your simple GDPR compliance script by:

  1. Setting up the folder structure
  2. Creating the folder structure
  3. Building the main app
  4. Creating the scripts

Setting up the folder structure

This is the final basic app structure:

/gdpr-circleci-demo
  │── .gitignore
  │── requirements.txt
  │
  │── .circleci/
  │   └── config.yml            # CircleCI pipeline config
  ├── src/
        │
        │── scripts/
        │   ├── generate_report.py    # GDPR compliance report
        │   ├── check-audit-logs.sh   # Audit log validation
        │   ├── check-consent-logs.sh   # Consent log validation
        │
        │── logs/
        │
        │
        └── app.py 

Get ready to build the app step by step.

First, create a repository for your project on GitHub. You can either do this manually through GitHub’s web interface, or using terminal:

# Create a new local folder
mkdir gdpr-circleci-demo
cd gdpr-circleci-demo

# Initialize a Git repository
git init

Next, create the GitHub repository online (if you haven’t yet). Connect your local project to the remote:

# Add your GitHub repository as a remote (replace URL with your actual repo URL)
git remote add origin https://github.com/your-username/your-repo-name.git

You are ready to push your code whenever you want to commit a change. Once the CI/CD pipeline is integrated, each commit you push to your repo will trigger the pipeline, running the checks you have in place, so you can monitor for any breakages.

Creating the folder structure

Inside your project root, create the necessary folders and basic files.

mkdir src && cd src
touch app.py

Within the src folder, add a logs folder, where the checks log will be generated, a scripts folder, for scripts to automate compliance checks, and a generate_report.py file:

mkdir scripts
mkdir logs
touch scripts/check-audit-logs.sh
touch scripts/check-consent-logs.sh
touch scripts/generate_report.py

Ultimately, add a .gitignore:

touch .gitignore

Inside of it, add the following:

__pycache__/
*.pyc
logs/

In the project’s root, also add a CircleCI configuration. You will fill in the config.yml with the pipeline configuration (we’ll explore the full config later).

cd ..
mkdir -p .circleci
touch .circleci/config.yml

Now that the setup is complete, you can move on to building your Python application.

Building the main app

As you are simulating a GDPR compliance routine, you need to build some mock logs to ensure your scripts can read them correctly.

Within the app.py file, you log each event, generate the compliance report and print relevant info to the terminal. In your app.py, write the following:

import os
import time

# Paths to logs 
logs_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logs')
audit_log_file = os.path.join(logs_directory, 'audit.log')
consent_log_file = os.path.join(logs_directory, 'consent.log')

# Ensure the 'logs' directory exists
if not os.path.exists(logs_directory):
    os.makedirs(logs_directory)

def log_audit_event(user, event_type):
    """Log an audit event."""
    timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
    log_entry = f'{timestamp} - {user} performed {event_type}\n'

    with open(audit_log_file, 'a') as log_file:
        log_file.write(log_entry)
    print(f"Audit event logged: {log_entry.strip()}")

def log_consent_event(user, consent_type):
    """Log a consent event."""
    timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
    log_entry = f'{timestamp} - {user} consented to {consent_type}\n'

    with open(consent_log_file, 'a') as log_file:
        log_file.write(log_entry)
    print(f"Consent event logged: {log_entry.strip()}")

def main():
    """Run the mock app."""
    # Simulate logging user actions and consent
    log_audit_event("John Doe", "accessed sensitive data")
    log_audit_event("Jane Smith", "accessed public data")

    log_consent_event("John Doe", "privacy policy")
    log_consent_event("Jane Smith", "terms of service")

if __name__ == '__main__':
    main()

Creating the scripts

To fully automate the compliance process, you need to check the generated logs for relevant entries and generate a final compliance report using scripts/check-audit-logs.sh. To check if the audit log contains events related to sensitive data access, add this code to your script:

#!/bin/bash

AUDIT_LOG="src/logs/audit.log"

# Check if the audit log contains entries related to sensitive data access
if grep -q "sensitive" "$AUDIT_LOG"; then
  echo "Audit log contains sensitive data access. Audit check passed."
  exit 0
else
  echo "Audit log does not contain sensitive data access. Audit check failed."
  exit 1
fi

Add the following to the consent logs script to check if the consent log contains entries related to privacy policy consent:

#!/bin/bash

CONSENT_LOG="src/logs/consent.log"

# Check if the consent log contains  entries related to privacy policy consent
if grep -q "privacy" "$CONSENT_LOG"; then
  echo "Consent log contains privacy policy consent. Consent check passed."
  exit 0
else
  echo "Consent log does not contain privacy policy consent. Consent check failed."
  exit 1
fi

Add the following to your generate_report.py to generate a final compliance report that will be saved in your /logs folder.

import os

src_directory = os.path.dirname(os.path.abspath(__file__)) 
logs_directory = os.path.join(src_directory, '..', 'logs')
audit_log_file = os.path.join(logs_directory, 'audit.log')
consent_log_file = os.path.join(logs_directory, 'consent.log')
report_file = os.path.join(logs_directory, 'compliance_report.txt')

def generate_compliance_report():
    """Generate a compliance report based on the logs."""
    report = []  # List to accumulate the report content

    report.append("\nGenerating compliance report...\n")

    # Read logs
    try:
        with open(audit_log_file, 'r') as audit_file:
            audit_logs = audit_file.readlines()

        with open(consent_log_file, 'r') as consent_file:
            consent_logs = consent_file.readlines()
    except FileNotFoundError as e:
        print(f"Error: {e}")
        return

    # Check for relevant events in audit and consent logs
    relevant_audit_events = [log for log in audit_logs if 'sensitive' in log]
    relevant_consent_events = [log for log in consent_logs if 'privacy' in log]

    # Add compliance report details to the report list
    report.append("\nCompliance Report")
    report.append(f"Total Audit Events: {len(audit_logs)}")
    report.append(f"Relevant Audit Events: {len(relevant_audit_events)}")
    report.append(f"Total Consent Events: {len(consent_logs)}")
    report.append(f"Relevant Consent Events: {len(relevant_consent_events)}")

    # Compliance Check Summary
    if relevant_audit_events and relevant_consent_events:
        report.append("\nCompliance passed: Relevant audit and consent data found.")
    else:
        report.append("\nCompliance failed: Missing relevant audit or consent data.")

    # Adding more detailed report output
    report.append("\nDetailed Log Entries:")

    if relevant_audit_events:
        report.append("\nRelevant Audit Events (sensitive data access):")
        for event in relevant_audit_events:
            report.append(f"- {event.strip()}")

    if relevant_consent_events:
        report.append("\nRelevant Consent Events (privacy policy consent):")
        for event in relevant_consent_events:
            report.append(f"- {event.strip()}")

    # Debugging: Check the path before writing
    print("Writing report to:", report_file)

    # Save the report to a file
    with open(report_file, 'w') as report_file_handle:
        report_file_handle.write("\n".join(report))
        print("Report written successfully.")

if __name__ == '__main__':
    generate_compliance_report()

To create a report, run the app.py script first, then move to the /scripts folder and run the generate_report.py script.

cd .. #to move to the src folder
python app.py
cd scripts #back to the scripts folder
python generate_report.py

Here is an example of a successful report:

Generating compliance report...

Compliance Report
Total Audit Events: 14
Relevant Audit Events: 7
Total Consent Events: 14
Relevant Consent Events: 7

Compliance passed: Relevant audit and consent data found.

Detailed Log Entries:

Relevant Audit Events (sensitive data access):
- 2025-04-13 12:14:22 - John Doe performed accessed sensitive data
- 2025-04-28 15:08:43 - John Doe performed accessed sensitive data
- 2025-04-28 15:10:13 - John Doe performed accessed sensitive data
- 2025-04-28 15:10:46 - John Doe performed accessed sensitive data
- 2025-04-28 15:12:05 - John Doe performed accessed sensitive data
- 2025-04-28 15:12:11 - John Doe performed accessed sensitive data
- 2025-04-28 15:12:35 - John Doe performed accessed sensitive data

Relevant Consent Events (privacy policy consent):
- 2025-04-13 12:14:22 - John Doe consented to privacy policy
- 2025-04-28 15:08:43 - John Doe consented to privacy policy
- 2025-04-28 15:10:13 - John Doe consented to privacy policy
- 2025-04-28 15:10:46 - John Doe consented to privacy policy
- 2025-04-28 15:12:05 - John Doe consented to privacy policy
- 2025-04-28 15:12:11 - John Doe consented to privacy policy
- 2025-04-28 15:12:35 - John Doe consented to privacy policy

Setting up the CI/CD pipeline

CI/CD stands for continuous integration (CI) and continuous delivery/deployment (CD). CI/CD pipelines are designed to automate and support the software development lifecycle, taking care of more daunting tasks like building, testing, and deploying your code.

CI/CD pipelines can be incredibly useful for automating security testing, privacy audits, and compliance reporting. These can help developers by ensuring that code changes meet GDPR requirements and detecting vulnerabilities early in the process.

In this context, CircleCI is a great tool for the job. On this platform, you can easily automate privacy checks, security scans, and other compliance checks, integrate your workflows with GitHub for version control, and you can ascertain that critical compliance tasks run safely before deployment.

Pipeline configuration: Understanding CircleCI’s config.yml file

In this tutorial, you are setting up a CI/CD pipeline using CircleCI for a Python program, focusing on integrating security and privacy checks.

In your .circleci/config.yml file, add the following:

version: 2.1

jobs:
  run_mock_python_app:
    docker:
      - image: cimg/python:3.8
    steps:
      - checkout
      - run:
          name: Run mock app to generate logs
          command: |
            mkdir -p src/logs
            python src/app.py
      - persist_to_workspace:
          root: .
          paths:
            - src/logs

  audit_check:
    docker:
      - image: cimg/python:3.8
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: Check audit logs
          command: bash src/scripts/check-audit-logs.sh

  consent_check:
    docker:
      - image: cimg/python:3.8
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: Check consent logs
          command: bash src/scripts/check-consent-logs.sh

  generate_report:
    docker:
      - image: cimg/python:3.8
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: Generate compliance report
          command: python src/scripts/generate_report.py
      - store_artifacts:
          path: src/logs
          destination: logs

workflows:
  version: 2
  gdpr_pipeline:
    jobs:
      - run_mock_python_app
      - audit_check:
          requires:
            - run_mock_python_app
      - consent_check:
          requires:
            - run_mock_python_app
      - generate_report:
          requires:
            - audit_check
            - consent_check

The config.yml file is central to this process, as it defines the pipeline structure. In it, you must specify:

  • The jobs or tasks that the pipeline will execute

  • The steps within each job

  • The workflow determining when and how the jobs are executed

Jobs

These could include security testing, privacy checks, and generating compliance reports. For this example, we’re creating a basic Python app that logs mock events and jobs to scan the logs to ensure proper handling of sensitive data, and verify GDPR compliance.

version: 2.1

jobs:
  run_mock_python_app:
    docker:
      - image: cimg/python:3.8
    steps:
      - checkout
      - run:
          name: Run mock app to generate logs
          command: |
            mkdir -p src/logs
            python src/app.py
      - persist_to_workspace:
          root: .
          paths:
            - src/logs

Here, you are using a Python 3.8 Docker container to run your mock Python app to generate logs. You will do the same for audit_check, consent_check, and generate_report.

Steps

Steps (what happens inside a job) may include installing dependencies, running tests, generating compliance reports, and deploying the application in the context of your Python web app.

Below is an example consent check step that checks for GDPR violations:

  steps:
    - checkout
    - attach_workspace:
        at: .
    - run:
        name: Check consent logs
        command: bash src/scripts/check-consent-logs.sh

Workflow

Workflows organize jobs and determine in which order they are executed.

The below workflow ensures that security and privacy checks pass before deployment. We ensure the app runs first, so that the logs are generated previous to the scripts checking for violantions and errors.

workflows:
  version: 2
  gdpr_pipeline:
    jobs:
      - run_mock_python_app
      - audit_check:
          requires:
            - run_mock_python_app
      - consent_check:
          requires:
            - run_mock_python_app
      - generate_report:
          requires:
            - audit_check
            - consent_check

If these security and privacy checks don’t pass, the deploy job won’t run.

To automate this process, you need to connect CircleCI to GitHub. Link your GitHub repository to CircleCi, add the .circleci/config.yml file to the repository. CircleCI will automatically detect every time you push an update to the repository and trigger a pipeline run.

Integrating GitHub

To automate this process, you need to connect CircleCI to GitHub. To do this, add the .circleci/config.yml file to the repository using:

git add . && git commit -am "Added CircleCI config file"

Push the repository to GitHub and the create a project in your CircleCI account.

CircleCI will automatically detect every time you push an update to the repository and trigger a pipeline run.

With this setup, every code change will be checked for GDPR compliance before deployments.

Security testing steps

You can add extra security scanning to your program. For this, you’ll need Snyk and a requirements.txt (you can generate it using the command: pip freeze > requirements.txt).

  • Ensure the project is set up with CircleCI and your security test tool.

  • Update your .circleci/config.yml file with a new job for security testing. Here is an example using Snyk:


dependency_scan:
    docker:
      - image: cimg/python:3.8
    steps:
      - checkout

      - run:
          name: Install Node.js 14.x and Snyk
          command: |
            sudo curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -
            sudo apt-get update
            sudo apt-get install -y nodejs
            sudo npm install -g snyk --unsafe-perm

      - run:
          name: Authenticate Snyk
          command: snyk auth "$SNYK_TOKEN"

      - run:
          name: Install Python dependencies
          command: pip install -r requirements.txt

      - run:
          name: Run Snyk Security Scan
          command: snyk test --file=requirements.txt --package-manager=pip

This job will run Snyk against your app and check your dependencies for vulnerabilities. Don’t forget to update your workflows accordingly:

workflows:
  version: 2
  gdpr_pipeline:
    jobs:
      - run_mock_python_app
      - audit_check:
          requires:
            - run_mock_python_app
      - consent_check:
          requires:
            - run_mock_python_app
      - generate_report:
          requires:
            - audit_check
            - consent_check
      - dependency_scan:  # Here
          requires:
            - run_mock_python_app

Additional privacy checks

To set up additional privacy check jobs in CircleCI, create a new job in your .circleci/config.yml file for each privacy concern you want to address automatically. Below are a few examples:

As per GDPR requirements, users need to give clear consent before their data is collected and processed. In this simple example below, automated checks for consent management check that the consent withdrawal mechanism works properly. You can also add jobs checking that the consent mechanism is providing clear banners about user content, ensuring that the given consent is logged away in a traceable way, and testing easy withdrawal and audit logs.

verify_withdrawal_mechanism:
    docker:
      - image: cimg/base:stable
    steps:
      - run:
          name: Validate consent management
          command: bash src/scripts/verify_withdrawal_mechanism.sh

Third-party data handling

Third-party service providers processing personal data must also comply with regulations. Adding this job to your configuration file allows you to check that the third-party services you are using are fully compliant and have data processing agreements (DPAs) in place. You are creating a job that checks if the consent log contains entries related to third-party services:

third_party_data_handling:
  docker:
    - image: cimg/base:stable
  steps:
    - run:
        name: Validate third-party data handling
        command: bash src/scripts/third-party-data-handling.sh

Don’t forget to update workflows:


workflows:
  version: 2
  gdpr_pipeline:
    jobs:
      - run_mock_python_app
      - audit_check:
          requires:
            - run_mock_python_app
      - consent_check:
          requires:
            - run_mock_python_app
      - generate_report:
          requires:
            - audit_check
            - consent_check
      - dependency_scan:
          requires:
            - run_mock_python_app
      - verify_withdrawal_mechanism:  # Here
          requires:
            - run_mock_python_app
      - third_party_data_handling:   # And here
          requires:
            - run_mock_python_app

app.py and generate_report.py should also be updated to generate logs for the new checks:

Add src/app.py

# ...
# Additional logs
withdrawal_mechanism_log = os.path.join(logs_directory, 'consent_banner_check.log')
third_party_analytics_log = os.path.join(logs_directory, 'third_party_analytics.log')

# ...

def log_verify_withdrawal_mechanism(user, consent_type):
    """Log a consent withdrawal event."""
    timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
    log_entry = f'{timestamp} - {user} withdrawn consent for {consent_type}\n'

    with open(consent_log_file, 'a') as log_file:
        log_file.write(log_entry)
    print(f"Consent withdrawal logged: {log_entry.strip()}")

def log_third_party_analytics_check():
    """Log a third-party analytics check."""
    timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
    log_entry = f'{timestamp} - Third-party analytics check passed\n'

    with open(third_party_analytics_log, 'a') as log_file:
        log_file.write(log_entry)
    print(f"Third-party analytics check logged: {log_entry.strip()}")

def main():
  # ...
    log_consent_event("John Doe", "Google Analytics") # here
    log_consent_event("John Doe", "Facebook Pixel") # here

    # Simulate the consent banner and third-party analytics checks
    log_verify_withdrawal_mechanism("John Doe", "privacy policy")
    log_third_party_analytics_check()

Add scripts/generate_report.py

import os

src_directory = os.path.dirname(os.path.abspath(__file__)) 
logs_directory = os.path.join(src_directory, '..', 'logs')
audit_log_file = os.path.join(logs_directory, 'audit.log')
consent_log_file = os.path.join(logs_directory, 'consent.log')
report_file = os.path.join(logs_directory, 'compliance_report.txt')

# Additional checks
verify_withdrawal_mechanism = os.path.join(logs_directory, 'verify_withdrawal_mechanism.log')
third_party_analytics_log = os.path.join(logs_directory, 'third_party_analytics.log')

def generate_compliance_report():
    """Generate a compliance report based on the logs."""
    ...

    # Check for relevant events in audit and consent logs
    relevant_audit_events = [log for log in audit_logs if 'sensitive' in log]
    relevant_consent_events = [log for log in consent_logs if 'privacy' in log]
    consent_withdrawals = [log for log in consent_logs if 'withdrawn' in log]

    #....

    if consent_withdrawals:
      report.append("\nConsent Withdrawal Events:")
      for event in consent_withdrawals:
          report.append(f"- {event.strip()}")

    # ...

    # Check for additional logs like withdrawal mechanism check and third-party analytics
    if os.path.exists(verify_withdrawal_mechanism):
        with open(verify_withdrawal_mechanism, 'r') as file:
            banner_check = file.read().strip()
        report.append(f"\nConsent Banner Check: {banner_check}")

    if os.path.exists(third_party_analytics_log):
        with open(third_party_analytics_log, 'r') as file:
            third_party_check = file.read().strip()
        report.append(f"\nThird-Party Analytics Check: {third_party_check}")

    # Debugging: Check the path before writing
    print("Writing report to:", report_file)

    # Save the report to a file
    with open(report_file, 'w') as report_file_handle:
        report_file_handle.write("\n".join(report))
        print("Report written successfully.")

if __name__ == '__main__':
    generate_compliance_report()

Now that the Python app is ready to handle these new scripts and the jobs are in place, add them to your /scripts folder:

Add scripts/third-party-data-handling.sh

#!/bin/bash

CONSENT_LOG="src/logs/consent.log"

# Check if the consent log contains entries related to third-party services
if grep -qi "google analytics" "$CONSENT_LOG"; then
  echo "Consent for Google Analytics found. Third-party data handling check passed."
else
  echo "No consent for Google Analytics found. Third-party data handling check failed."
  exit 1
fi

# Check for Facebook Pixel consent
if grep -qi "facebook pixel" "$CONSENT_LOG"; then
  echo "Consent for Facebook Pixel found. Third-party data handling check passed."
else
  echo "No consent for Facebook Pixel found. Third-party data handling check failed."
  exit 1
fi

echo "Third-Party Data Handling Validation passed."

Add scripts/verify_withdrawal_mechanism.sh

#!/bin/bash

CONSENT_LOG="src/logs/consent.log"

# Check if the consent log file contains withdrawal events (e.g., user withdrawal of consent)
if grep -q "withdrawn" "$CONSENT_LOG"; then
  echo "Consent withdrawal mechanism exists. Withdrawal check passed."
else
  echo "Consent withdrawal mechanism not found. Withdrawal check failed."
  exit 1
fi

echo "Consent Withdrawal Mechanism Check passed."

Example pipeline in action

To review the pipeline in action, use the same tools from the previous examples, and all scripts included so far.

You will also need a Snyk API token to pass to CircleCI:

  1. Go to your Snyk account settings.

  2. Copy your Snyk API token by clicking Account settings -> General -> Auth Token.

  3. In your CircleCI project:

Go to Project SettingsEnvironment Variables. Add the variable by entering SNYK_TOKEN in the Name field. Add your token in the Value field.

Your project structure (with additional checks)

/gdpr-circleci-demo
  │── .gitignore
  │── requirements.txt
  │
  │── .circleci/
  │   └── config.yml            # CircleCI pipeline config
  ├── src/
        │
        │── scripts/
        │   ├── generate_report.py    # GDPR compliance report
        │   ├── check-audit-logs.sh   # Audit log validation
        │   ├── check-consent-logs.sh   # Consent log validation
        │   ├── third-party-data-handling.sh   # Check for entries related to third-party services
        │   └── verify-withdrawal-mechanism.sh   # Check for withdrawal events
        │
        │── logs/
        │
        │
        └── app.py 

Run the pipeline in CircleCI

After connecting your GitHub repository to CircleCI, the pipeline will be triggered with any push.

You should be able to see every job’s status, real-time logs for each step, and be able to access a compliance_report.json file.

With your .circleci/config.yml file set up, your pipeline will automatically run when you push a commit to your repository.

Depending on how workflows are defined, each job in your file is executed in parallel or in a sequence. With parallel execution, all checks can run simultaneously. With sequential execution, certain jobs, such as compliance reporting generation, can be set to take place after the privacy and security checks.

CircleCI Interface feedback (visuals & logs)

CircleCI UI provides real-time feedback on the status of each job:

🔴 Red (Failed): If a security scan detects a vulnerability.

🟢 Green (Passed): If all checks succeed. Here is a link to the repo with the mock Python app that simulates GDPR compliance logging and reporting.

It tracks user interactions with sensitive data (audit logs) and records user consent (consent logs), storing them in separate log files. It also checks for entries related to third-party services and withdrawal events, and it scans requirements.txt for dependency vulnerability.

The CircleCI pipeline performs automated checks on audit and consent logs, verifying compliance criteria (such as presence of required log entries), and outputs results in the UI with status indicators for each job.

Here’s how it works step-by-step:

  1. Runs mock Python app – Generates audit.log and consent.log.

  2. Audit checks – Validates the presence and content of audit.log.

  3. Consent check – Validates the presence and content of consent.log.

  4. Dependency scan – Scans requirements.txt for vulnerabilities with Snyk.

  5. Third party data check – Check for entries related to third-party services.

  6. Verifies withdrawal mechanism – Check for consent withdrawal events.

  7. Report generation – Consolidates logs into a compliance report.

  8. Each step is visible as a separate job in the CircleCI UI with its own status and logs for easier debugging.

Green checks for basic GDPR compliance app

Automating GDPR compliance with CircleCI - basic checks

Green checks for extended version with additional checks

Automating GDPR compliance with CircleCI - additional checks

Best practices and tips for GDPR automation

  • Stay up to date with GDPR as regulations evolve: you might need to stay on top of emerging enforcement guidelines.

  • Automate dependency updates to make sure libraries are current.

  • Enhance security by storing credentials using CircleCI’s Environment Variables, limiting who can access sensitive configurations, and regularly auditing secrets exposure.

  • Monitor third-party dependencies that process user data and verify their GDPR compliance, and automate license and DPA (Data Processing Agreement) checks.

  • Regularly run jobs to delete or anonymize personal data past its retention period, and ensure encryption where necessary.

Conclusion

GDPR compliance in modern web applications is of paramount importance and automating checks and security can remove a lot of manual effort around continuous security and privacy enforcement. Integrating security testing with tools like Snyk, automating privacy checks, handling data subject requests, and generating compliance reports are a few of the actions you can take to support compliance.

Compliance is an ongoing process, so make sure to regularly update security tools and policies to adapt to new threats and regulations. CircleCI’s automation features help developers stay compliant effortlessly while focusing on building applications.

Resources and further reading