# Inspection and DLP

> Which outbound requests of a CI job Egress CI scans for secrets, what inspect changes, and the trade-off of terminating TLS in a job.

Egress CI scans what a job sends for secrets with the data-leak detectors of the [OpenWAF Egress](https://developers.openwaf.dev/egress/#what-it-detects) gateway. In enforce a request that carries one is refused; in audit it passes and the summary lists the detection.

## What is detected

| Detection id | What |
|---|---|
| `dlp.private_key` | A PEM private key (`-----BEGIN … PRIVATE KEY-----`) |
| `dlp.aws_access_key` | An AWS access key id |
| `dlp.openai_key` | An OpenAI-style `sk-` key |
| `dlp.github_token` | A GitHub token, except when it goes to GitHub (`github.com`, `githubusercontent.com`, `ghcr.io`, `githubapp.com` and their subdomains), where the job's own token is expected |
| `dlp.credit_card` | A payment card number that passes the Luhn check |
| `dns.high_entropy_label` | A DNS label of 40 characters or more with high entropy: data encoded into a name |
| `dns.long_name` | A name of 150 characters or more |
| `tls.sni_mismatch` | A `CONNECT` whose TLS SNI names a host outside the policy |

The `dns.` detections are flagged, not refused by themselves: the name is still answered or refused by the policy. A secret inside a DNS name or a `CONNECT` host (`dlp.…` on the name) is detected on every destination, because the name is visible without decrypting anything.

## What is scanned

| Request | Scanned |
|---|---|
| Plain HTTP through `HTTPS_PROXY`/`HTTP_PROXY` | URL, headers and body |
| Plain HTTP caught by the lock (a tool that ignores the proxy) | The first bytes of the connection: request line, headers and the start of the body |
| HTTPS to a host under `inspect`, through `HTTPS_PROXY` | URL, headers and body of every request |
| Any other HTTPS | Only the name (`CONNECT` host, SNI, DNS name), not the content |

Bodies are scanned whole up to 4 MiB. Larger bodies, and binary ones (`application/octet-stream`, `image/*`, `video/*`, zip, gzip, tar), pass unscanned, and the event says so.

## inspect

```yaml
allow:
  - api.staging.example.com
inspect:
  - api.staging.example.com
```

For a host under `inspect`, the proxy terminates the job's TLS with a certificate issued by a CA created for the run, scans each request, and sends it on over a new TLS connection to the real host, verifying that host's certificate as usual. Inspection works on HTTP/1.1 (the proxy offers only `http/1.1` in ALPN) and on connections made through `HTTPS_PROXY`; a tool that ignores the proxy variables reaches the host through the transparent proxy, which checks the name but does not decrypt.

So that the job trusts the run's CA, the action writes its certificate to `$RUNNER_TEMP/egress-ci/ca.pem` and a bundle with the system CAs, and sets:

| Variable | Read by |
|---|---|
| `NODE_EXTRA_CA_CERTS` | Node.js |
| `SSL_CERT_FILE` | OpenSSL-based tools, Go, Ruby, Python's `ssl` |
| `REQUESTS_CA_BUNDLE` | Python Requests |
| `CURL_CA_BUNDLE` | curl |
| `GIT_SSL_CAINFO` | Git |

## The TLS trade-off

Inspecting means the job accepts a CA that can sign a certificate for any name, for as long as the run lasts. The action limits what that costs:

- The CA is created at the start of each run, valid for 12 hours, and only for that run. Its private key never leaves the sidecar's memory; only the certificate is written.
- It signs certificates only for the hosts under `inspect`; every other HTTPS connection stays an opaque tunnel.
- The real host's certificate is still verified by the sidecar, so inspecting does not hide a bad certificate from you: the request fails.

And what it does not solve:

- Tools with their own trust store (a JVM's `cacerts`, some language package managers) or with certificate pinning fail on inspected hosts. Leave those hosts out of `inspect`.
- Containers do not get the variables; inspection is for the job's own steps.
- Without `inspect`, the content of HTTPS requests is never seen. A secret sent over HTTPS to an allowed host is not detected: the allowlist is the main control, DLP is a second line for plain HTTP and for the hosts you choose to inspect.

Inspect the hosts where a secret in a body would matter and that your tools reach through the proxy: a staging API your tests post to, a webhook, an artifact or telemetry endpoint. Do not inspect registries or GitHub.
