OTel PII & Secrets Leak Detector avatar

OTel PII & Secrets Leak Detector

Pricing

from $250.00 / 1,000 telemetry batch scanneds

Go to Apify Store
OTel PII & Secrets Leak Detector

OTel PII & Secrets Leak Detector

Scan OpenTelemetry traces, metrics and logs for leaked secrets and PII before they reach your observability backend. Get a risk report, a Collector redaction config and sanitized telemetry. Standby HTTP API included.

Pricing

from $250.00 / 1,000 telemetry batch scanneds

Rating

0.0

(0)

Developer

Justin Hagerty

Justin Hagerty

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

12 days ago

Last modified

Share

Scan OpenTelemetry traces, logs and metrics for API keys, tokens, passwords, emails, card numbers and other personal data before they reach your observability backend. You get a risk report, a ready-to-review OpenTelemetry Collector redaction config, and a sanitized copy of your telemetry.

Telemetry leaks quietly. An HTTP instrumentation captures the Authorization header. An exception message echoes a database URL with its password. A developer adds user.email to a span "just for debugging", or a log line prints a whole request body containing a card number. A few weeks later that data has been copied to every observability backend, retained for 30 to 400 days and made searchable by hundreds of engineers and third-party vendors.

This Actor reads OTLP/JSON (the format the Collector file exporter, otlphttp with JSON encoding and SDK debug exporters write) and runs 66 rules against every value: resource and scope attributes, span names and attributes, span events (exception.message, exception.stacktrace), links, status messages, log bodies (including nested structured bodies), log attributes, metric names and descriptions, data-point attributes and exemplars. For each leak it tells you which service, scope and attribute key leaks, how often, with what confidence and with which potential regulatory exposure. It then generates the Collector processors that remove the leak, and checks its own output by re-scanning.

The analysis is deterministic. It uses no LLMs and makes no network calls, and raw values are never stored: findings carry redacted previews and salted fingerprints only.


Why use it

  • Find leaks before your vendor does. Secrets in telemetry are a common incident source, and a quiet one: logs and traces are rarely access-controlled like databases.
  • Reduce compliance exposure. Personal data in telemetry brings your observability stack into scope for GDPR, CCPA, HIPAA or PCI DSS. The report labels potential exposure per regime (this is not legal advice).
  • Fix it, not just find it. You get an attributes + transform (OTTL) + redaction processor config wired into a full pipeline, plus SDK-side hints to stop the leak at the source.
  • Prove the fix. The sanitized copy of your telemetry is re-scanned (self-check), and the generated Collector config is emulated against your data, so the report lists exactly which leaks the config would still miss (for example, keys nested inside structured log bodies).
  • Low noise. Checksums (Luhn, IBAN mod-97, ABA, Spanish DNI, Dutch BSN, Brazilian CPF, Aadhaar Verhoeff, PESEL), SSA invalid ranges, numbering-plan rules and context requirements filter out look-alikes such as order IDs, timestamps, trace IDs, UUIDs, pagination tokens and part numbers. On a ~200,000-value synthetic corpus of realistic benign telemetry, no high-confidence finding was a false positive (see Limitations). Well-known test values (4111 1111 1111 1111, AKIAIOSFODNN7EXAMPLE, example.com, 555-01xx, documentation IP ranges) are labelled as test values.

Who it's for

  • Platform / observability teams rolling out OpenTelemetry who need a pre-production gate for new instrumentation.
  • Security and privacy engineers auditing what actually ends up in Datadog, Grafana, Honeycomb, New Relic, Elastic, Splunk and similar backends.
  • Teams in regulated industries (health, fintech, e-commerce) who must keep PHI and cardholder data out of logs.
  • Pipelines and AI agents that call it over HTTP (Standby mode) to check a telemetry sample before shipping a change.

What it detects

Group (detectors)RulesExamples
secretsLEAK-001 – LEAK-021, LEAK-062 – LEAK-066AWS access/secret keys, GCP service-account keys, Google API keys, Azure account keys and SAS signatures, GitHub/GitLab/Slack/Stripe/Twilio/SendGrid/OpenAI/Anthropic tokens, npm/PyPI tokens, New Relic/Grafana/Datadog keys, Shopify tokens, HashiCorp Vault (hvs./hvb./hvr.) and Terraform Cloud tokens, Hugging Face tokens, Databricks PATs, Mailgun keys (with a Mailgun label), PEM private keys, JWTs (decoded structurally to report the algorithm and which PII claims they carry — never verified), high-entropy values in secret-named attributes, password=/api_key: assignments in free text
credentialsLEAK-022 – LEAK-031Captured Authorization, Cookie/Set-Cookie, x-api-key-style headers; Bearer/Basic credentials in messages (Basic is decoded to confirm user:password); session IDs; passwords in URLs (scheme://user:pass@host) and connection strings; tokens in URL query strings (url.full, http.url, http.target); IDENTIFIED BY '…' / PASSWORD '…' / Redis AUTH literals in db.statement / db.query.text; password-named attributes
pii-contactLEAK-032 – LEAK-035Emails (also URL-encoded %40), phone numbers (E.164, international, North-American with numbering-plan checks), street addresses (heuristic), names in name-labelled attributes
pii-identifiersLEAK-036 – LEAK-042, LEAK-059 – LEAK-061US SSNs (SSA rules), checksum-verified national IDs (ES DNI/NIE, NL BSN, UK NINO when labelled or printed AB 12 34 56 C, Brazilian CPF, Indian Aadhaar, Polish PESEL), passport / driver's licence / tax IDs, dates of birth, user.* / enduser.* identity attributes, IMEI (Luhn), advertising IDs
networkLEAK-043 – LEAK-045End-user IPs in client.address, source.address, X-Forwarded-For and similar (public vs. private told apart; personal data under GDPR), public IPs elsewhere, MAC addresses
financialLEAK-046 – LEAK-049Card numbers (Luhn + issuer prefix/length + printed grouping), CVV/expiry attributes, IBANs (mod-97), bank account / ABA routing numbers
healthLEAK-050 – LEAK-052ICD-10 codes in diagnosis context, medical record / patient / health-plan IDs, health terms near patient context (PHI heuristic)
locationLEAK-053Precise coordinates (≥ 3 decimals)
free-text (off by default)LEAK-054 – LEAK-055Names in prose (honorifics, customer: First Last), request/response/message bodies captured as attributes
—LEAK-056Your own customPatterns
—LEAK-057 – LEAK-058Placement: sensitive values in span/metric names (can't be deleted, explode cardinality) or in resource attributes (copied onto every signal)

Every rule, with severity, rationale and references, is listed in RULES.md.

How to use

In Apify Console

  1. Open the Actor and paste an OTLP/JSON export into Telemetry (the prefilled demo uses obviously fake values). You can also paste JSON Lines from the Collector file exporter into Telemetry as text.
  2. Optionally pick detector groups, regimes, allowlists and custom patterns.
  3. Run. The Findings view lists each leak by signal/service/attribute. The Risk report view shows category counts, top leaking keys and potential regulatory exposure. Collector config & sanitized telemetry has the YAML and the cleaned copy.

Capture a sample from your Collector

exporters:
file/sample:
path: /tmp/otel-sample.jsonl # OTLP/JSON, one export request per line
service:
pipelines:
traces: { receivers: [otlp], processors: [batch], exporters: [file/sample, otlp/backend] }
logs: { receivers: [otlp], processors: [batch], exporters: [file/sample, otlp/backend] }

Prefer a staging environment with synthetic traffic (see Data handling).

Via the API (curl)

curl -s -X POST "https://api.apify.com/v2/acts/michiganpublictech~otel-pii-leak-detector/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d @- <<'EOF'
{
"telemetry": {
"resourceLogs": [{
"resource": {"attributes": [{"key": "service.name", "value": {"stringValue": "checkout"}}]},
"scopeLogs": [{"logRecords": [
{"body": {"stringValue": "payment failed for jane.doe@example.com card 4111 1111 1111 1111"},
"attributes": [{"key": "http.request.header.authorization", "value": {"stringValue": "Basic ZGVtbzpkZW1v"}}]}
]}]
}]
},
"redactionStrategy": "balanced"
}
EOF

Python (apify-client)

import json
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
batches = [json.loads(line) for line in open("/tmp/otel-sample.jsonl")]
run = client.actor("michiganpublictech/otel-pii-leak-detector").call(
run_input={"telemetryBatches": batches[:20], "failOnSeverity": "high"}
)
for report in client.dataset(run["defaultDatasetId"]).iterate_items():
print(report["target"], report["risk_level"], report["summary"])
for f in report["findings"]:
print(" ", f["severity"], f["rule_id"], f["location"])

Standby mode (HTTP API for pipelines and AI agents)

The Actor supports Actor Standby. Apify keeps a warm instance behind an HTTPS endpoint, so calls skip container start-up. Charging is identical to normal runs.

BASE=https://michiganpublictech--otel-pii-leak-detector.apify.actor
# Free: help, rule catalog, detector groups
curl -s "$BASE/" -H "Authorization: Bearer $APIFY_TOKEN"
curl -s "$BASE/rules" -H "Authorization: Bearer $APIFY_TOKEN"
# Scan: POST a raw OTLP/JSON document...
curl -s -X POST "$BASE/scan" -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
--data @otlp-export.json
# ...or an input-shaped object with options
curl -s -X POST "$BASE/scan" -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
-d '{"telemetry": {"spans": [{"name": "GET /x", "attributes": {"url.full": "https://a.example.com/?token=abc123def456"}}]},
"detectors": ["secrets", "credentials"], "emitSanitizedTelemetry": false}'
# → {"items": [<report>], "charged": {"telemetry-scanned": 1, "sanitized-output": 1}}

Endpoints: GET / (health/help; also answers the readiness probe), GET /rules, GET /detectors, POST /scan. Request bodies are limited to 8 MB. Options you don't send fall back to the Standby run's input. In Standby, results are returned in the HTTP response only and nothing is written to a dataset. The server never logs request bodies.

CI: block leaky instrumentation in pull requests

Run your integration tests with the Collector file exporter, then:

- name: Scan telemetry sample for PII & secrets
run: |
jq -Rs '{telemetryText: ., failOnSeverity: "high", sampleRetention: "none"}' otel-sample.jsonl > input.json
curl -sf -X POST "https://api.apify.com/v2/acts/michiganpublictech~otel-pii-leak-detector/runs?token=${{ secrets.APIFY_TOKEN }}&waitForFinish=300" \
-H "Content-Type: application/json" -d @input.json | jq -e '.data.status == "SUCCEEDED"'

failOnSeverity marks the run as FAILED when any finding reaches that severity. REPORT.sarif can be uploaded to GitHub code scanning.

Input

FieldTypeDefaultDescription
telemetryobject (or list)demoOne OTLP/JSON export request (resourceSpans / resourceLogs / resourceMetrics; snake_case and legacy instrumentationLibrary* keys accepted), or a simplified {resource, scope, spans, logs, metrics} object with plain attribute maps. A list counts as several batches.
telemetryBatchesarray—Several batches; each an OTLP/JSON document or {"name": "...", "telemetry": {...}}. One report per batch.
telemetryTextstring—OTLP/JSON text, a JSON array, or JSON Lines (one batch per line). Invalid lines become free failed reports.
detectorsarrayall except free-textDetector groups to run (see the table above).
regimesarrayallGDPR, CCPA, HIPAA, PCI_DSS — only used to label potential exposure.
minConfidencelow/medium/highlowDrop detections below this confidence.
testValuesdowngrade/report/ignoredowngradeKnown test values are reported with LOW severity by default.
allowlistAttributesarray—Keys never reported; exact or glob (app.public_*), matched against the full key and its last segment.
allowlistPatternsarray—Regexes; a detection whose text (or whole value) fully matches is ignored. ReDoS-screened.
customPatternsarray—Up to 20 {name, pattern, severity, action} detectors. ≤ 300 chars; nested quantifiers (a+)+, overlapping repeated alternations `(a
emitSanitizedTelemetrybooleantrueReturn a sanitized copy plus a re-scan self-check.
generateCollectorConfigbooleantrueGenerate the Collector redaction config, SDK hints and the coverage emulation.
redactionStrategybalanced/mask/hash/dropbalancedbalanced: delete credential headers, hash identifiers (user.email, client.address…), mask the rest. hash: hash whole values where possible (keeps joinability). drop: delete leaking attributes and emit a redaction allowed_keys allowlist.
collectorFlavorcontrib/contrib-legacycontribTarget otelcol-contrib version family: contrib for current releases (verified 0.148, 0.150), contrib-legacy for 0.115-era Collectors (see Collector version notes).
hashSaltsecret stringrandom per runSalts fingerprints and [HASH:…] tokens so they're comparable across runs.
sampleRetentionredacted/noneredactednone removes previews and fingerprints from findings (counts and locations only).
minSeverityseverityinfoHide findings below this severity (risk counts still include them).
failOnSeveritynone/critical/high/medium/lownoneCI gate.
disabledRulesarray—Rule IDs to skip (Pro plan and above).
licenseKeysecret string—Enterprise / prepaid license key. Store users don't need one.

Output

One dataset item per batch, using the same report shape as all our Actors. The key-value store also gets OUTPUT (run summary), REPORT.md and REPORT.sarif. Abridged example from the prefilled demo:

{
"product": "otel-pii-leak-detector",
"target": "telemetry",
"score": 26,
"risk_level": "critical",
"summary": {"critical": 2, "high": 6, "medium": 6, "low": 10, "info": 0},
"findings": [
{
"rule_id": "LEAK-022",
"title": "Authorization header captured as an attribute",
"severity": "critical",
"kind": "observation",
"category": "credential",
"location": "traces/checkout/span.attributes[\"http.request.header.authorization\"]",
"confidence": 0.95,
"evidence": {
"occurrences": 1, "distinct_values": 1, "confidence": "high", "detector": "authorization-header",
"scopes": ["io.opentelemetry.http"],
"example_path": "$.resourceSpans[0].scopeSpans[0].spans[0].attributes[2].value.arrayValue.values[0].stringValue",
"potential_regulatory_exposure": ["CCPA"],
"redacted_previews": ["Bear…Ww (187 chars)"], "value_fingerprints": ["5d1c0a9e44b2"],
"details": {"scheme": ["bearer"]}
},
"recommendation": "Remove Authorization from captured request headers and delete the attribute in the Collector; …"
},
{
"rule_id": "LEAK-017", "title": "JWT payload carries personal data claims", "severity": "high",
"evidence": {"details": {"pii_claims": ["email", "name"]}, "redacted_previews": ["eyJ…"], "...": "..."}
},
{
"rule_id": "LEAK-046", "title": "Payment card number (PAN) in telemetry (known test value)", "severity": "low",
"location": "logs/payments/log.attributes[\"payment.card_number\"]",
"evidence": {"redacted_previews": ["************1111"], "known_test_value": true, "...": "..."}
}
],
"data": {
"signals": ["traces", "logs", "metrics"],
"scanned": {"spans": 2, "span_events": 1, "log_records": 2, "data_points": 1, "records": 5, "values_scanned": 34,
"bytes": 3459, "billable_units": 1},
"risk": {
"total_occurrences": 24,
"by_category": {"credential": {"findings": 2, "occurrences": 2, "max_severity": "critical"}, "...": {}},
"by_service": [{"service": "checkout", "occurrences": 15, "categories": ["credential", "secret", "..."],
"max_severity": "critical"}],
"top_leaking_keys": [{"signal": "traces", "location": "span.attributes[\"http.request.header.authorization\"]",
"occurrences": 3, "rules": ["LEAK-016", "LEAK-017", "LEAK-022"], "max_severity": "critical"}],
"regulatory_exposure": {"GDPR": {"potential_exposure": true, "occurrences": 18, "categories": ["..."]},
"PCI_DSS": {"potential_exposure": true, "...": "..."}},
"legal_notice": "Regulatory labels … indicate POTENTIAL exposure … not legal advice …"
},
"collector_config": "# Generated by OTel PII & Secrets Leak Detector — REVIEW BEFORE DEPLOYING.\n…",
"collector_config_coverage": {"residual_high_confidence": 3, "residual_locations": ["logs:payments:log.body.patient.dob", "..."]},
"sdk_hints": ["Don't capture credential-bearing HTTP headers: …"],
"sanitized_telemetry": {"resourceSpans": ["… same structure, values masked / hashed / deleted …"]},
"sanitization": {"values_changed": 18, "masked_spans": 16, "hashed_values": 5, "deleted_attributes": 1},
"self_check": {"residual_high_confidence": 0, "shared_secret_scanner_hits": 0, "passed": true}
},
"disclaimer": "Automated pattern-based scan of the telemetry you supplied; …"
}

Generated Collector config (excerpt)

processors:
memory_limiter: {check_interval: 1s, limit_percentage: 80, spike_limit_percentage: 25}
attributes/leak_keys:
actions:
- key: http.request.header.authorization
action: delete
transform/leak:
error_mode: ignore
trace_statements:
- context: span
statements:
- set(attributes["user.id"], SHA256(Concat([attributes["user.id"], "${env:PII_HASH_SALT}"], ""))) where attributes["user.id"] != nil
- replace_all_patterns(attributes, "value", "eyJ[A-Za-z0-9_-]{8,}[.]eyJ[A-Za-z0-9_-]{8,}[.][A-Za-z0-9_-]*", "[REDACTED:jwt]")
- replace_all_patterns(attributes, "value", "([?&;](access_token|...|api_key|...)=)[^&# ]+", "$${1}[REDACTED]")
- replace_pattern(name, "[A-Za-z0-9._%+-]+(@|%40)...", "[REDACTED:email]")
log_statements:
- context: log
statements:
- replace_pattern(body, "(AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}", "[REDACTED:aws_key]") where IsString(body)
redaction/leak:
allow_all_keys: true
ignored_keys: [user.id] # hashed keys: keep the hash instead of masking it to ****
blocked_values: ["(AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}", "…"]
blocked_key_patterns: ["(?i).*(authorization|password|passwd|pwd|passphrase).*"]
summary: info
batch: {send_batch_size: 8192, timeout: 5s}
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, attributes/leak_keys, transform/leak, redaction/leak, batch]
exporters: [otlp_grpc/backend] # contrib-legacy: otlp/backend

How it is built:

  • attributes/leak_keys deletes credential-bearing keys observed on spans, logs and data points.
  • transform/leak (OTTL) hashes identifiers with a salted SHA256(Concat([…, "${env:PII_HASH_SALT}"], "")) instead of deleting them, so you can still count distinct users. It deletes or masks keys where the attributes processor can't reach them (resource, scope, span events) and rewrites values with replace_all_patterns / replace_pattern (attributes, span names, status messages, string log bodies, metric descriptions).
  • redaction/leak is a safety net. Its blocked_values are the patterns above plus a baseline of high-impact secret patterns. It also gets blocked_key_patterns (contrib) and ignored_keys for the keys the transform hashed — without that, a blocked key pattern would overwrite the hash with ****.
  • With redactionStrategy: drop the redaction processor runs with allow_all_keys: false and an allowed_keys list of every key observed in your sample that didn't leak (resource and scope attributes, and log-body map keys for contrib, included; service.name and the other service.* identity keys always). Any key that is not on the list is removed, including resource attributes, so add the keys your services emit that the sample didn't contain.
  • All patterns are written in the RE2 ∩ Python subset without quotes or backslashes, so they survive YAML and OTTL quoting unchanged.
  • collector_config_coverage is a Python emulation of the config on your data, followed by a re-scan. It lists what the config would not catch (typically values inside array or map attributes, which neither OTTL's replace_all_patterns nor the redaction processor rewrite, and — on contrib-legacy — nested log-body maps). Fix those with the SDK hints. The emulation was checked against real Collectors (see below) and predicted their output exactly, apart from the hash format ([HASH:…] vs SHA-256 hex) and hashed arrays (the Collector hashes the whole stringified array).

Collector version notes

Generated configs were checked with otelcol-contrib validate on 0.115.1, 0.148.0 and 0.150.1, and run end to end (an otlpjsonfile receiver feeding the generated processors into a file exporter) on 0.115.1 and 0.150.1 with every redaction strategy. What we found:

0.115.1 (contrib-legacy)0.148.0 / 0.150.1 (contrib)
redaction processor in traces, logs and metrics pipelinesyesyes
blocked_key_patternsrejected ('' has invalid keys)yes
ignored_keys, allowed_keys, allow_all_keys, summaryyesyes
Redaction masks resource, span, log-record and data-point attributesyesyes
… also scope and span-event attributes, and log bodies (strings and nested maps/arrays)noyes
allow_all_keys: false removes non-listed keys from resource attributesyesyes (also scope, span-event, log-body keys)
Exporter typeotlp (no otlp_grpc)otlp_grpc (otlp logs a deprecation warning on 0.150.1)

Behaviour common to all three versions:

  • The OTTL used here parses in every version: set, delete_key, replace_pattern, replace_all_patterns(attributes, "value", …), SHA256(Concat([…], "")), IsString(body) and the context: + statements: block form (no deprecation warning on 0.150.1).
  • $$ escapes $ in Collector config files, so $${1} reaches OTTL as a ${1} capture-group reference. That worked as intended on all versions. Hashing needs the PII_HASH_SALT env var (alphanumeric); SHA256 produces 64 hex characters.
  • Neither the transform nor the redaction processor rewrites values inside array or map attributes, and the redaction processor never touches span names, status messages, links or metric descriptions (the transform covers those that leaked).
  • The redaction processor runs after the transform, so it can re-mask what the transform already replaced: on 0.150.1, password=[REDACTED] in a log body becomes **** because a blocked value matches it. The data stays hidden either way.

Pick contrib-legacy for 0.115-era Collectors and contrib for current releases. Versions between 0.116 and 0.147 were not tested: if validate rejects blocked_key_patterns or otlp_grpc, use contrib-legacy. Always review and run otelcol-contrib validate --config=<file> before deploying. Generated configs are a starting point, not a guarantee.

Regulatory mapping

RegimeLabelled when we detectBasis
GDPRcontact details, identifiers, IPs/MACs/device IDs, location, financial, health (Art. 9 special category), JWTs with PII claimspersonal data (Art. 4)
CCPA / CPRAthe above plus account credentials (password, auth headers, session tokens)personal and sensitive personal information
HIPAAhealth detectors (ICD-10, MRN, PHI heuristics); identifiers are labelled HIPAA only when health data appears in the same batchPHI = health information + identifiers
PCI DSScard numbers (PAN) and card verification datacardholder data / sensitive authentication data

Not legal advice. Labels mean potential exposure based on the data types detected in your sample. They are not a compliance assessment or certification. Involve your privacy, security and compliance teams.

Data handling

  • Prefer synthetic or sampled telemetry. Use staging traffic, test accounts and test cards where possible. The scanner tells you whether a field captures a kind of data even when the value is a known test value.
  • What we store: findings with redacted previews (for example AKIA…LE (20 chars), j***@***.com, ************1111; passwords always ****) and salted HMAC fingerprints (12 hex chars). Set sampleRetention: none to drop previews and fingerprints entirely. The sanitized telemetry contains masked/hashed values only, and the self-check verifies that. The generated config contains patterns, never your values.
  • What we never store or log: raw secret or PII values. Your input itself is stored by Apify as the run's INPUT record, like any Actor input.
  • Retention: Apify's default storage retention applies to the run's input, dataset and key-value store (unnamed storages expire automatically; see the Apify docs on data retention). Delete the run when you are done if your sample was sensitive.
  • hashSalt and licenseKey are secret inputs (encrypted by Apify) and never echoed into output.

Pricing

Pay per event, with no subscription.

EventPriceWhen
telemetry-scanned$0.25Per scanned batch. One event covers up to 5 MB of OTLP/JSON and up to 10,000 records (spans + log records + metric data points). Larger batches count max(ceil(MB / 5), ceil(records / 10,000)) events, up to 20 (bigger batches are refused for free; split them).
sanitized-output$0.10Per batch, only when a remediation pack (sanitized telemetry and/or Collector config) with at least one change was delivered.

Examples: one 300-span trace export with leaks costs $0.25 + $0.10 = $0.35. A clean batch costs $0.25. A 25,000-span batch with remediation costs 3 × $0.25 + $0.10 = $0.85. Batches that can't be parsed, and batches we refuse, are never charged. The Actor checks your spending limit before each batch and drops the remediation pack, rather than charging partially, if only the scan fits.

Plans and licensing

Store users need nothing extra. Sanitized telemetry and the generated config are included on the Apify Store (marketplace) plan. Self-hosted/community runs produce the report only. disabledRules needs a Pro license. Enterprise and prepaid keys go in licenseKey.

Limitations

  • Heuristic detection. Names, addresses and health context use heuristics (low/medium confidence), and novel token formats without a recognisable shape are only caught when the attribute name is secret-like. False positives and false negatives are possible. Check confidence on each finding.
  • Measured precision (synthetic data). On a generated corpus of ~197,000 benign values (trace IDs, UUIDs, digests, k8s names, versions, timestamps, base64 cursors, URLs, order and invoice numbers, part numbers, stack traces…) plus 603 planted leaks, every planted leak was found, no high-confidence finding was a false positive, and about 1 in 7,500 benign values raised a medium-confidence finding (mostly formatted CPF look-alikes). Real telemetry differs from synthetic data, so treat these figures as a regression baseline rather than a guarantee. Use minConfidence: high for the quietest output.
  • Only what you send. The report covers the sample, not everything your services emit. Rare code paths may leak in ways a sample doesn't show.
  • OTLP/JSON only. Protobuf, Jaeger and Zipkin formats aren't parsed. Use the Collector file exporter (JSON) to capture. bytesValue attributes are not decoded.
  • Values longer than 64 KiB are scanned up to that length, and at most 500,000 values per batch.
  • The Collector config can't reach everything. Nested map values and metric names need source fixes. The coverage emulation tells you which locations are affected.
  • Collector patterns have no checksums or context. OTTL/RE2 can't run Luhn, mod-97, mod-11 or Verhoeff, so the generated card, IBAN, SSN, CPF and national-ID patterns may also mask look-alike values (card patterns are restricted to issuer prefixes). The Mailgun pattern can mask key-<32 hex> cache keys. Review them against your data.

FAQ

Does it call any external service or LLM? No. Detection, JWT decoding and config generation are local and deterministic.

Why is 4111 1111 1111 1111 reported at all? It's a test value, so severity is lowered to LOW. But a card-shaped value in payment.card_number means the attribute captures card numbers, and real ones would leak the same way. Set testValues: ignore to hide them.

Will hashing user.email keep my dashboards working? Yes for counting and grouping, because the same input gives the same salted hash. Hashing is pseudonymisation, not anonymisation.

Why do I get both LEAK-022 and LEAK-016 for one header? LEAK-022 says the header is captured at all. LEAK-016/017 describe the token inside it (and whether its payload carries personal data).

Can I add my own identifiers? Yes: customPatterns (for example CUST-[0-9]{8}) with severity and action. They're ReDoS-screened and added to the generated config when RE2-compatible.

Is my data used for anything else? No. See Data handling.

Roadmap

Planned detectors, outputs and known caveats are tracked in STATUS.md.

Changelog

See CHANGELOG.md.