Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ Duplicate variable definitions inside env files (both main env and example env,

### 4 Secret Detection

Potential secrets and sensitive values, including high-risk patterns.
Potential secrets and sensitive values, including high-risk patterns. See [Security Scanner](./security_scanner.md) for a full description of detection techniques and false positive protections.

### 5 Example File Secret Warnings

Potential secrets found in `.env.example` content.
Potential secrets found in `.env.example` content. See the [Example File Scanning](./security_scanner.md#example-file-scanning) section of the Security Scanner docs.

### 6 Framework-Specific Misuse

Framework-aware warnings (for supported frameworks) around unsafe or incorrect env usage patterns.
Framework-aware warnings (for supported frameworks) around unsafe or incorrect env usage patterns. See [Framework Warnings](./frameworks/index.md).

### 7 Uppercase Naming Warnings

Expand All @@ -78,7 +78,7 @@ Cases where environment-related values are logged with `console.log`.

### 10 Expiration Warnings

Warnings for environment values that look like expiring tokens/credentials or contain expiration metadata.
Warnings for environment values that look like expiring tokens/credentials or contain expiration metadata. See [Expiration Warnings](./expiration_warnings.md).

### 11 Gitignore Safety Check

Expand Down
124 changes: 124 additions & 0 deletions docs/security_scanner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Security Scanner

`dotenv-diff` scans your source files and `.env.example` for hardcoded secrets using three complementary techniques.

## Table of Contents

- [Provider Pattern Matching](#1-provider-pattern-matching-high-severity)
- [Suspicious Key Name Detection](#2-suspicious-key-name-detection-medium-severity)
- [High-Entropy String Detection](#3-high-entropy-string-detection-medium--high-severity)
- [Example File Scanning](#example-file-scanning)
- [False Positive Protections](#false-positive-protections)
- [Suppressing False Positives](#suppressing-false-positives)

---

## 1. Provider Pattern Matching (high severity)

Detects known credential formats from popular providers:

| Provider | Example pattern |
|---|---|
| AWS access key | `AKIA` + 16 uppercase alphanumeric chars |
| AWS temporary key | `ASIA` + 16 uppercase alphanumeric chars |
| GitHub token | `ghp_` + 30+ alphanumeric chars |
| Stripe live secret | `sk_live_` + 24+ alphanumeric chars |
| Stripe test secret | `sk_test_` + 24+ alphanumeric chars |
| Google API key | `AIza` + 20+ alphanumeric chars |
| Google OAuth token | `ya29.` + alphanumeric chars |
| Firebase token | 21-char ID + `:` + 140-char token |
| JWT | Three base64url segments separated by `.` starting with `eyJ` |
| Twilio Account SID | `AC` + 32 hex chars |
| Ethereum address | `0x` + 40 hex chars |

---

## 2. Suspicious Key Name Detection (medium severity)

Flags literal string assignments where the variable or attribute name matches a sensitive pattern:

```
password, pass, secret, token, apikey, api_key, client_secret, access_token, access-token
```

Only triggers when all of the following are true:

- The value is **12+ characters** long
- The value does **not** contain spaces (space → likely a human-readable label, not a secret)
- The attribute name is **not** a known harmless UI prop (`label`, `placeholder`, `name`, `title`, `aria-label`, etc.)
- The line does **not** read from an env accessor (`process.env`, `import.meta.env`, SvelteKit `$env/*`)
- The value is **not** a pure interpolation template (e.g. `` `${a}:${b}` ``)

---

## 3. High-Entropy String Detection (medium / high severity)

Uses [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_(information_theory)) to detect randomly-generated secrets — strings that are statistically too random to be written by hand.

| String length | Entropy threshold | Severity |
|---|---|---|
| 32–47 chars | ≥ 0.85 (normalized) | medium |
| 48+ chars | ≥ 0.85 (normalized) | high |

> In test files (`*.spec.ts`, `*.test.ts`, `__tests__/`, `fixtures/`, etc.) the threshold is raised to **0.95** to reduce false positives.

---

## Example File Scanning

`.env.example` files are scanned separately with relaxed rules, since example files are expected to contain placeholder values. Entries are skipped when the value:

- Is empty
- Equals `example` or `placeholder` (case-insensitive)
- Contains `your_` or `CHANGE_ME`
- Contains `<` (typical for `<your-value-here>` style templates)

Remaining values are still checked against [provider patterns](#1-provider-pattern-matching-high-severity) and entropy (threshold ≥ 0.8 for values ≥ 24 characters).

---

## False Positive Protections

The scanner automatically skips values that are clearly not secrets:

| Pattern | Example |
|---|---|
| UUIDs | `550e8400-e29b-41d4-a716-446655440000` |
| Hex hashes | MD5, SHA-1, SHA-256 (32–128 hex chars) |
| Short base64 IDs | 16–20 char base64 strings |
| Data URIs | `data:image/png;base64,...` |
| Relative paths | `./assets/image.png` |
| SVG path data | `M10 20 L30 40 Z` |
| Character set literals | `abcdefghijklmnopqrstuvwxyz0123456789` (used with `nanoid` etc.) |
| UI label strings | Any value containing spaces |
| Minified lines | Lines over 500 characters are skipped entirely |
| Comment-only lines | Lines starting with `//` |
| Env accessors | `process.env.MY_KEY`, `import.meta.env.MY_KEY` |

---

## Suppressing False Positives

If a finding is a known false positive, suppress it with an ignore comment on the same line.

### Single line

```typescript
const apiKey = 'safe_value_for_tests_123123'; // dotenv-diff-ignore
```

```html
<a href="https://legacy.internal.com"> <!-- dotenv-diff-ignore -->
```

### Block

```html
<!-- dotenv-diff-ignore-start -->
<img src="https://cdn.safe-service.com/image.png" />
<!-- dotenv-diff-ignore-end -->
```

Ignore markers are **case-insensitive** and support `//`, `/* */`, and `<!-- -->` comment styles.

For broader suppression across files or URL patterns, see [Ignore Comments](./ignore_comments.md) and [Configuration and Flags](./configuration_and_flags.md).
2 changes: 2 additions & 0 deletions packages/cli/src/core/helpers/isLikelyMinified.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* Returns true if a line looks like minified/bundled code.
* Used to skip entire files early in the pipeline.
* @param content - The content of the file to check
* @returns Boolean whether the content is likely minified
*/
export function isLikelyMinified(content: string): boolean {
return content.split(/\r?\n/).some((line) => line.length > 500);
Expand Down