Checks

The active detectors Centaur runs against every repository. Each one cites a specific regulation and shows the exact pattern we look for.

Tracking scripts loading before consent

GDPR Art. 6(1)(a)ePrivacy Directive Art. 5(3)CCPA §1798.135

Tracking and analytics scripts must not execute before the user has given valid, informed consent.

Violation
<!-- Violation: Google Analytics runs on every page load,
     before any cookie banner appears. -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<script>
  gtag('config', 'G-XXXX');
</script>
Compliant
// Compliant: tracker only fires after the user opts in.
if (hasUserConsented('analytics')) {
  loadGoogleAnalytics();
}

Pre-ticked consent checkboxes

GDPR Art. 7GDPR Recital 32ePrivacy Directive Art. 5(3)CCPA §1798.140(l)

Consent checkboxes must be unchecked by default; pre-ticked boxes do not constitute valid consent.

Violation
<!-- Violation: analytics consent is pre-ticked. -->
<input type="checkbox" name="analytics" checked />
<label>I agree to analytics cookies</label>
Compliant
<!-- Compliant: unchecked by default; user must actively opt in. -->
<input type="checkbox" id="analytics-consent" />
<label for="analytics-consent">I agree to analytics cookies</label>

Ad SDKs in child-directed apps without COPPA flags

Ad SDKs integrated into child-directed apps must be initialised with child-directed treatment flags enabled.

Violation
// Violation: AdMob initialised without child-directed flag
// in an app whose manifest declares it kids-directed.
MobileAds.initialize(this) { /* no COPPA config */ }
Compliant
// Compliant: child-directed treatment enabled before init.
RequestConfiguration config = new RequestConfiguration.Builder()
    .setTagForChildDirectedTreatment(
        RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)
    .build();
MobileAds.setRequestConfiguration(config);
MobileAds.initialize(this, status -> {});

PII in log statements

Personal data must not be written to log files; use non-identifying surrogates (e.g. user ID, masked values).

Violation
# Violation: email address written into log output.
logger.info(f"User: {user.email}")
Compliant
# Compliant: opaque user ID only.
logger.info(f"User {user.id} logged in")

Plaintext or weak password hashing

Passwords must be hashed with a memory-hard algorithm (bcrypt, argon2id, or scrypt). MD5 and SHA1 are prohibited for password storage. Authentication tokens and API keys should use a keyed hash (HMAC-SHA256) or an opaque random ID.

Violation
# Violation: MD5 applied to a password.
import hashlib
hashed = hashlib.md5(password.encode()).hexdigest()
Compliant
# Compliant: bcrypt with a per-password salt.
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))

Hardcoded secrets in source code

Credentials, API keys, and secrets must not be hardcoded in source files; load them from environment variables or a secrets manager instead.

Violation
# Violation: AWS access key checked into source.
AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"
AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
Compliant
# Compliant: read from environment.
import os
AWS_ACCESS_KEY_ID = os.environ["AWS_ACCESS_KEY_ID"]

EU / CN / IN user data resources deployed to wrong cloud region

Personal data belonging to EU, Chinese, or Indian residents must be stored and processed only in approved geographic regions.

Violation
# Violation: EU-tagged bucket deployed in us-east-1.
provider "aws" { region = "us-east-1" }

resource "aws_s3_bucket" "eu_user_data" {
  bucket = "acme-eu-user-vault"
  tags   = { Jurisdiction = "EU", DataClass = "PII" }
}
Compliant
# Compliant: EU data stored in an EU region.
provider "aws" { region = "eu-west-1" }

resource "aws_s3_bucket" "eu_user_data" {
  bucket = "acme-eu-user-vault"
  tags   = { Jurisdiction = "EU", DataClass = "PII" }
}