envshield 4.7.3__tar.gz → 4.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {envshield-4.7.3 → envshield-4.8.0}/PKG-INFO +90 -10
- {envshield-4.7.3 → envshield-4.8.0}/README.md +89 -9
- envshield-4.8.0/envshield/__init__.py +1 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/cli.py +280 -113
- {envshield-4.7.3 → envshield-4.8.0}/envshield/config/manager.py +468 -65
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/contract_diff.py +133 -51
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/dependency_diff.py +24 -3
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/dependency_snapshot.py +26 -6
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/discovery.py +312 -21
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/doctor.py +66 -48
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/enforcement.py +6 -0
- envshield-4.8.0/envshield/core/evaluator.py +598 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/exceptions.py +116 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/explain.py +62 -92
- envshield-4.8.0/envshield/core/file_updater.py +296 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/generator.py +31 -15
- envshield-4.8.0/envshield/core/hooks_manager.py +669 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/importer.py +15 -2
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/scanner.py +197 -504
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/schema_manager.py +255 -108
- envshield-4.8.0/envshield/core/schema_scope.py +296 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/schema_snapshot.py +66 -6
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/schema_types.py +139 -24
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/service_discovery.py +39 -9
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/setup_manager.py +54 -24
- envshield-4.8.0/envshield/core/source_files.py +124 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/parsers/_base.py +6 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/parsers/_docker_compose.py +32 -13
- {envshield-4.7.3 → envshield-4.8.0}/envshield.egg-info/PKG-INFO +90 -10
- {envshield-4.7.3 → envshield-4.8.0}/envshield.egg-info/SOURCES.txt +3 -0
- {envshield-4.7.3 → envshield-4.8.0}/pyproject.toml +1 -1
- envshield-4.7.3/envshield/__init__.py +0 -1
- envshield-4.7.3/envshield/core/file_updater.py +0 -135
- envshield-4.7.3/envshield/core/hooks_manager.py +0 -170
- {envshield-4.7.3 → envshield-4.8.0}/LICENSE.md +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/__main__.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/classifier/__init__.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/classifier/context_classifier.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/classifier/tokenizer.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/config/__init__.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/__init__.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/inspector.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/core/service_manager.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/parsers/__init__.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/parsers/_deployment.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/parsers/_dotenv.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/parsers/_kubernetes.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/parsers/_python.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/parsers/factory.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/state.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/utils/__init__.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/utils/git_utils.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield/utils/paths.py +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield.egg-info/dependency_links.txt +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield.egg-info/entry_points.txt +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield.egg-info/requires.txt +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/envshield.egg-info/top_level.txt +0 -0
- {envshield-4.7.3 → envshield-4.8.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: envshield
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.8.0
|
|
4
4
|
Summary: EnvShield: environment variables as a version-controlled configuration contract.
|
|
5
5
|
Author-email: Rabbil Yasar Sajal <rabbilyasar@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -165,10 +165,11 @@ What each field actually means:
|
|
|
165
165
|
| `enum` | Value must be one of this list. Implies an enum type regardless of `type`. |
|
|
166
166
|
| `pattern` | A regex the value must also match, e.g. `pattern = "^v\\d+\\.\\d+\\.\\d+$"`. |
|
|
167
167
|
| `secret` | Marks the variable sensitive — masked input in `setup`, masked in generated code. `import` auto-suggests it from the variable's name/value; it never overwrites a `secret` value you've already committed. |
|
|
168
|
-
| `defaultValue` | What `setup` writes automatically. A variable still has to be explicitly present in your local file
|
|
168
|
+
| `defaultValue` | What `setup` writes automatically (and `.env.example` and generated code show). A default never makes a variable optional: it still has to be explicitly present in your local file — `check`/`doctor` name the default inline so a missing one is obvious. |
|
|
169
|
+
| `required` | Optional. `required = false` makes a variable optional: it may be absent or blank, but a value that is set must still be valid. `required = true` is the default. Can't be combined with `requiredIf`. |
|
|
169
170
|
| `description` | Shown in `setup`, copied into generated code. |
|
|
170
171
|
|
|
171
|
-
|
|
172
|
+
Requiredness: every variable is required unless `required = false`, or `requiredIf` makes it conditional (see below). Any other key, or an unknown `type`, is an error — a typo like `default = "8000"` is reported (`did you mean 'defaultValue'?`) instead of silently ignored.
|
|
172
173
|
|
|
173
174
|
### Conditional requirements (`requiredIf`)
|
|
174
175
|
|
|
@@ -210,7 +211,7 @@ Four stages, in order: define the contract, validate against it, discover what c
|
|
|
210
211
|
|
|
211
212
|
### Validate
|
|
212
213
|
|
|
213
|
-
**`envshield check`** — validate local configuration, and commonly-used Docker Compose and Kubernetes deployment-manifest patterns, against the same schema (see [Known limitations](#known-limitations)).
|
|
214
|
+
**`envshield check`** — validate local configuration, and commonly-used Docker Compose and Kubernetes deployment-manifest patterns, against the same schema (see [Known limitations](#known-limitations)). It also scans the service's own source code and fails on a variable the code reads but the schema doesn't declare (the same files the pre-commit `scan` checks). `--process-env` also counts variables set in your shell (for a name the schema declares, the shell's value wins over the file's). `--json` adds a versioned, value-free report per service.
|
|
214
215
|
|
|
215
216
|
```bash
|
|
216
217
|
$ envshield check
|
|
@@ -270,7 +271,7 @@ Every command above documents its own options with `--help`; the full reference
|
|
|
270
271
|
|
|
271
272
|
## Git / CI workflow
|
|
272
273
|
|
|
273
|
-
This is where the schema stops being documentation and starts being enforcement. Three concrete things EnvShield catches
|
|
274
|
+
This is where the schema stops being documentation and starts being enforcement. Three concrete things EnvShield catches before a change reaches production:
|
|
274
275
|
|
|
275
276
|
**A new environment variable used in code but missing from the schema:**
|
|
276
277
|
|
|
@@ -284,12 +285,14 @@ envshield undeclared origin/main HEAD --service api
|
|
|
284
285
|
envshield schema diff origin/main HEAD --service api
|
|
285
286
|
```
|
|
286
287
|
|
|
287
|
-
**A secret accidentally introduced into a commit
|
|
288
|
+
**A secret accidentally introduced into a commit:**
|
|
288
289
|
|
|
289
290
|
```bash
|
|
290
291
|
envshield hook install
|
|
291
292
|
```
|
|
292
293
|
|
|
294
|
+
The pre-commit hook runs `scan --staged --enforce`, applying classification-based enforcement with interactive override for high-confidence findings on local commits, and strict blocking in CI. See [Secret safety](#secret-safety) for enforcement behavior details.
|
|
295
|
+
|
|
293
296
|
In CI, diff against the merge-base, not the base branch's current tip (which can move while the PR is open):
|
|
294
297
|
|
|
295
298
|
```yaml
|
|
@@ -466,10 +469,84 @@ Source-code discovery (what powers `undeclared` and `explain`'s "used in source"
|
|
|
466
469
|
|
|
467
470
|
## Secret safety
|
|
468
471
|
|
|
469
|
-
A supporting check alongside the contract, not the product itself. `secret = true` on a field tells EnvShield (and `setup`, and code generation) that it's sensitive — the schema records that fact, never the actual value.
|
|
472
|
+
A supporting check alongside the contract, not the product itself. `secret = true` on a field tells EnvShield (and `setup`, and code generation) that it's sensitive — the schema records that fact, never the actual value.
|
|
473
|
+
|
|
474
|
+
### Secret scanning
|
|
475
|
+
|
|
476
|
+
**`envshield scan`** looks for hardcoded secrets by pattern (Stripe, AWS, GitHub tokens, and more), and for env-var reads the schema doesn't declare, in one pass. Values are always redacted in output — a Stripe *publishable* key (`pk_...`) is never flagged, since detection matches the secret-key pattern (`sk_...`) by shape, not the variable's name.
|
|
477
|
+
|
|
478
|
+
### Context-aware classification
|
|
479
|
+
|
|
480
|
+
Python candidates are analyzed for syntactic context to distinguish code patterns from secret values:
|
|
481
|
+
|
|
482
|
+
```python
|
|
483
|
+
# Classified as code (suppressed):
|
|
484
|
+
client(key=CONFIG_CONSTANT)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def handler(api_key: str): ...
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
# Classified as likely secret (detected):
|
|
491
|
+
client(key="sk_live_abc123...")
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
This reduces false positives on ordinary code patterns without broadly suppressing secret-shaped values. Classification is lightweight and syntactic — it recognizes type annotations, function arguments, and identifier references, but doesn't perform semantic analysis beyond what the tokenizer can see.
|
|
495
|
+
|
|
496
|
+
### Git hook enforcement
|
|
497
|
+
|
|
498
|
+
**`envshield hook install`** wires secret scanning into your Git workflow with classification-based enforcement:
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
envshield hook install
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
The pre-commit hook runs `scan --staged --enforce`, which scans only staged content and applies an enforcement policy based on classification confidence.
|
|
505
|
+
|
|
506
|
+
#### Enforcement behavior
|
|
507
|
+
|
|
508
|
+
| Finding type | Interactive (local commit) | Non-interactive (CI) |
|
|
509
|
+
|---|---|---|
|
|
510
|
+
| No findings | Allow | Allow |
|
|
511
|
+
| Clearly code-shaped | Suppressed | Suppressed |
|
|
512
|
+
| Ambiguous | Block | Block |
|
|
513
|
+
| High-confidence secret | Block + explicit override | Block |
|
|
514
|
+
|
|
515
|
+
**Clearly code-shaped** candidates (type annotations, function keyword arguments, etc.) are suppressed by the classifier and don't produce findings.
|
|
516
|
+
|
|
517
|
+
**Ambiguous** findings — insufficient context to confidently classify — block without override in both modes.
|
|
518
|
+
|
|
519
|
+
**High-confidence secrets** (string literals matching secret patterns) block by default but offer an interactive override for local commits:
|
|
520
|
+
|
|
521
|
+
```
|
|
522
|
+
⚠ EnvShield found potential secrets
|
|
523
|
+
|
|
524
|
+
HIGH CONFIDENCE
|
|
525
|
+
src/config.py:42 Generic API Key [REDACTED (30 chars)]
|
|
526
|
+
|
|
527
|
+
[1] Abort commit
|
|
528
|
+
[2] Commit anyway
|
|
529
|
+
|
|
530
|
+
Choice: 2
|
|
531
|
+
|
|
532
|
+
Type COMMIT ANYWAY to confirm: COMMIT ANYWAY
|
|
533
|
+
|
|
534
|
+
Proceeding with commit. The finding remains in the repository history.
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
The override requires exact confirmation text — `yes` or `Y` won't work. EOF (Ctrl+D) or Ctrl+C aborts. Raw secret values are never displayed in enforcement output.
|
|
538
|
+
|
|
539
|
+
In CI or other non-interactive contexts, high-confidence findings block with no prompt — the commit is refused, and the scan exits non-zero.
|
|
540
|
+
|
|
541
|
+
#### Scanning modes
|
|
542
|
+
|
|
543
|
+
```bash
|
|
544
|
+
envshield scan # Audit entire codebase
|
|
545
|
+
envshield scan --staged # Scan only staged Git content
|
|
546
|
+
envshield scan --staged --enforce # Staged scan + enforcement policy
|
|
547
|
+
```
|
|
470
548
|
|
|
471
|
-
-
|
|
472
|
-
- **`envshield hook install`** wires `scan --staged` into a pre-commit hook, so a real secret is caught before it's committed, not after.
|
|
549
|
+
The pre-commit hook uses the third form. `scan` without flags is for repository-wide audits, not commit-time enforcement.
|
|
473
550
|
|
|
474
551
|
---
|
|
475
552
|
|
|
@@ -493,12 +570,15 @@ envshield uninstall --yes # skip the confirmation prompt
|
|
|
493
570
|
|
|
494
571
|
## Known limitations
|
|
495
572
|
|
|
496
|
-
EnvShield is built around the patterns real projects actually use. A few narrower cases aren't handled yet
|
|
573
|
+
EnvShield is built around the patterns real projects actually use. A few narrower cases aren't handled yet. None of them let a secret leak; the first one can let a value EnvShield couldn't see pass `check`:
|
|
497
574
|
|
|
575
|
+
- **Unverified manifest values pass the v4 gate.** When a deployment manifest only *points* at a value — a Kubernetes `secretKeyRef`/`configMapKeyRef` to an object outside the file, a Secret key in the same file, a Compose `${VAR}` or bare `KEY`, or a `${VAR-x}`/`${VAR:-x}` fallback — EnvShield can't see the deployed value. `check` reports these variables as unverified (`valid: null`, status `not_visible`, listed in `summary.unverified`, and named in the human output), but they still pass: exit code and `success` are unchanged. A fallback that itself violates the contract still fails. Making the gate act on unverified values is planned for v5.
|
|
498
576
|
- **Docker Compose:** `${VAR}`, `${VAR:-default}`, and the no-colon `${VAR-default}` form are all supported. The `${VAR:?}`/`${VAR:+}` forms and embedded/multiple interpolation references in one value aren't evaluated yet.
|
|
499
577
|
- **Kubernetes:** `env[].valueFrom.secretKeyRef`/`configMapKeyRef` are matched by the container's environment-variable name (`env[].name`) — name your schema variable after the env var, not the key — and, when the referenced Secret/ConfigMap is defined in the same manifest, the referenced key is validated to actually exist there. `envFrom.prefix` is applied. `initContainers` are selectable via `--container` but never auto-selected by default.
|
|
500
578
|
- **Source discovery:** Python is AST-based; JavaScript/TypeScript is pattern-based. No other language is discovered yet.
|
|
501
579
|
- **.env:** multiline quoted values aren't supported.
|
|
580
|
+
- **Windows:** the write-safety fix for a failed permission change (BL-162) hasn't been run on Windows; where `os.fchmod` is missing (Windows before Python 3.13) it's skipped, which is tested on Linux by simulation only.
|
|
581
|
+
- **CI:** runs on Ubuntu only (Python 3.10–3.12).
|
|
502
582
|
|
|
503
583
|
These are bounded, tracked engineering work — see the [roadmap](ROADMAP.md) for what's next and what EnvShield deliberately isn't building.
|
|
504
584
|
|
|
@@ -106,10 +106,11 @@ What each field actually means:
|
|
|
106
106
|
| `enum` | Value must be one of this list. Implies an enum type regardless of `type`. |
|
|
107
107
|
| `pattern` | A regex the value must also match, e.g. `pattern = "^v\\d+\\.\\d+\\.\\d+$"`. |
|
|
108
108
|
| `secret` | Marks the variable sensitive — masked input in `setup`, masked in generated code. `import` auto-suggests it from the variable's name/value; it never overwrites a `secret` value you've already committed. |
|
|
109
|
-
| `defaultValue` | What `setup` writes automatically. A variable still has to be explicitly present in your local file
|
|
109
|
+
| `defaultValue` | What `setup` writes automatically (and `.env.example` and generated code show). A default never makes a variable optional: it still has to be explicitly present in your local file — `check`/`doctor` name the default inline so a missing one is obvious. |
|
|
110
|
+
| `required` | Optional. `required = false` makes a variable optional: it may be absent or blank, but a value that is set must still be valid. `required = true` is the default. Can't be combined with `requiredIf`. |
|
|
110
111
|
| `description` | Shown in `setup`, copied into generated code. |
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
Requiredness: every variable is required unless `required = false`, or `requiredIf` makes it conditional (see below). Any other key, or an unknown `type`, is an error — a typo like `default = "8000"` is reported (`did you mean 'defaultValue'?`) instead of silently ignored.
|
|
113
114
|
|
|
114
115
|
### Conditional requirements (`requiredIf`)
|
|
115
116
|
|
|
@@ -151,7 +152,7 @@ Four stages, in order: define the contract, validate against it, discover what c
|
|
|
151
152
|
|
|
152
153
|
### Validate
|
|
153
154
|
|
|
154
|
-
**`envshield check`** — validate local configuration, and commonly-used Docker Compose and Kubernetes deployment-manifest patterns, against the same schema (see [Known limitations](#known-limitations)).
|
|
155
|
+
**`envshield check`** — validate local configuration, and commonly-used Docker Compose and Kubernetes deployment-manifest patterns, against the same schema (see [Known limitations](#known-limitations)). It also scans the service's own source code and fails on a variable the code reads but the schema doesn't declare (the same files the pre-commit `scan` checks). `--process-env` also counts variables set in your shell (for a name the schema declares, the shell's value wins over the file's). `--json` adds a versioned, value-free report per service.
|
|
155
156
|
|
|
156
157
|
```bash
|
|
157
158
|
$ envshield check
|
|
@@ -211,7 +212,7 @@ Every command above documents its own options with `--help`; the full reference
|
|
|
211
212
|
|
|
212
213
|
## Git / CI workflow
|
|
213
214
|
|
|
214
|
-
This is where the schema stops being documentation and starts being enforcement. Three concrete things EnvShield catches
|
|
215
|
+
This is where the schema stops being documentation and starts being enforcement. Three concrete things EnvShield catches before a change reaches production:
|
|
215
216
|
|
|
216
217
|
**A new environment variable used in code but missing from the schema:**
|
|
217
218
|
|
|
@@ -225,12 +226,14 @@ envshield undeclared origin/main HEAD --service api
|
|
|
225
226
|
envshield schema diff origin/main HEAD --service api
|
|
226
227
|
```
|
|
227
228
|
|
|
228
|
-
**A secret accidentally introduced into a commit
|
|
229
|
+
**A secret accidentally introduced into a commit:**
|
|
229
230
|
|
|
230
231
|
```bash
|
|
231
232
|
envshield hook install
|
|
232
233
|
```
|
|
233
234
|
|
|
235
|
+
The pre-commit hook runs `scan --staged --enforce`, applying classification-based enforcement with interactive override for high-confidence findings on local commits, and strict blocking in CI. See [Secret safety](#secret-safety) for enforcement behavior details.
|
|
236
|
+
|
|
234
237
|
In CI, diff against the merge-base, not the base branch's current tip (which can move while the PR is open):
|
|
235
238
|
|
|
236
239
|
```yaml
|
|
@@ -407,10 +410,84 @@ Source-code discovery (what powers `undeclared` and `explain`'s "used in source"
|
|
|
407
410
|
|
|
408
411
|
## Secret safety
|
|
409
412
|
|
|
410
|
-
A supporting check alongside the contract, not the product itself. `secret = true` on a field tells EnvShield (and `setup`, and code generation) that it's sensitive — the schema records that fact, never the actual value.
|
|
413
|
+
A supporting check alongside the contract, not the product itself. `secret = true` on a field tells EnvShield (and `setup`, and code generation) that it's sensitive — the schema records that fact, never the actual value.
|
|
414
|
+
|
|
415
|
+
### Secret scanning
|
|
416
|
+
|
|
417
|
+
**`envshield scan`** looks for hardcoded secrets by pattern (Stripe, AWS, GitHub tokens, and more), and for env-var reads the schema doesn't declare, in one pass. Values are always redacted in output — a Stripe *publishable* key (`pk_...`) is never flagged, since detection matches the secret-key pattern (`sk_...`) by shape, not the variable's name.
|
|
418
|
+
|
|
419
|
+
### Context-aware classification
|
|
420
|
+
|
|
421
|
+
Python candidates are analyzed for syntactic context to distinguish code patterns from secret values:
|
|
422
|
+
|
|
423
|
+
```python
|
|
424
|
+
# Classified as code (suppressed):
|
|
425
|
+
client(key=CONFIG_CONSTANT)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def handler(api_key: str): ...
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
# Classified as likely secret (detected):
|
|
432
|
+
client(key="sk_live_abc123...")
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
This reduces false positives on ordinary code patterns without broadly suppressing secret-shaped values. Classification is lightweight and syntactic — it recognizes type annotations, function arguments, and identifier references, but doesn't perform semantic analysis beyond what the tokenizer can see.
|
|
436
|
+
|
|
437
|
+
### Git hook enforcement
|
|
438
|
+
|
|
439
|
+
**`envshield hook install`** wires secret scanning into your Git workflow with classification-based enforcement:
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
envshield hook install
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
The pre-commit hook runs `scan --staged --enforce`, which scans only staged content and applies an enforcement policy based on classification confidence.
|
|
446
|
+
|
|
447
|
+
#### Enforcement behavior
|
|
448
|
+
|
|
449
|
+
| Finding type | Interactive (local commit) | Non-interactive (CI) |
|
|
450
|
+
|---|---|---|
|
|
451
|
+
| No findings | Allow | Allow |
|
|
452
|
+
| Clearly code-shaped | Suppressed | Suppressed |
|
|
453
|
+
| Ambiguous | Block | Block |
|
|
454
|
+
| High-confidence secret | Block + explicit override | Block |
|
|
455
|
+
|
|
456
|
+
**Clearly code-shaped** candidates (type annotations, function keyword arguments, etc.) are suppressed by the classifier and don't produce findings.
|
|
457
|
+
|
|
458
|
+
**Ambiguous** findings — insufficient context to confidently classify — block without override in both modes.
|
|
459
|
+
|
|
460
|
+
**High-confidence secrets** (string literals matching secret patterns) block by default but offer an interactive override for local commits:
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
⚠ EnvShield found potential secrets
|
|
464
|
+
|
|
465
|
+
HIGH CONFIDENCE
|
|
466
|
+
src/config.py:42 Generic API Key [REDACTED (30 chars)]
|
|
467
|
+
|
|
468
|
+
[1] Abort commit
|
|
469
|
+
[2] Commit anyway
|
|
470
|
+
|
|
471
|
+
Choice: 2
|
|
472
|
+
|
|
473
|
+
Type COMMIT ANYWAY to confirm: COMMIT ANYWAY
|
|
474
|
+
|
|
475
|
+
Proceeding with commit. The finding remains in the repository history.
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The override requires exact confirmation text — `yes` or `Y` won't work. EOF (Ctrl+D) or Ctrl+C aborts. Raw secret values are never displayed in enforcement output.
|
|
479
|
+
|
|
480
|
+
In CI or other non-interactive contexts, high-confidence findings block with no prompt — the commit is refused, and the scan exits non-zero.
|
|
481
|
+
|
|
482
|
+
#### Scanning modes
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
envshield scan # Audit entire codebase
|
|
486
|
+
envshield scan --staged # Scan only staged Git content
|
|
487
|
+
envshield scan --staged --enforce # Staged scan + enforcement policy
|
|
488
|
+
```
|
|
411
489
|
|
|
412
|
-
-
|
|
413
|
-
- **`envshield hook install`** wires `scan --staged` into a pre-commit hook, so a real secret is caught before it's committed, not after.
|
|
490
|
+
The pre-commit hook uses the third form. `scan` without flags is for repository-wide audits, not commit-time enforcement.
|
|
414
491
|
|
|
415
492
|
---
|
|
416
493
|
|
|
@@ -434,12 +511,15 @@ envshield uninstall --yes # skip the confirmation prompt
|
|
|
434
511
|
|
|
435
512
|
## Known limitations
|
|
436
513
|
|
|
437
|
-
EnvShield is built around the patterns real projects actually use. A few narrower cases aren't handled yet
|
|
514
|
+
EnvShield is built around the patterns real projects actually use. A few narrower cases aren't handled yet. None of them let a secret leak; the first one can let a value EnvShield couldn't see pass `check`:
|
|
438
515
|
|
|
516
|
+
- **Unverified manifest values pass the v4 gate.** When a deployment manifest only *points* at a value — a Kubernetes `secretKeyRef`/`configMapKeyRef` to an object outside the file, a Secret key in the same file, a Compose `${VAR}` or bare `KEY`, or a `${VAR-x}`/`${VAR:-x}` fallback — EnvShield can't see the deployed value. `check` reports these variables as unverified (`valid: null`, status `not_visible`, listed in `summary.unverified`, and named in the human output), but they still pass: exit code and `success` are unchanged. A fallback that itself violates the contract still fails. Making the gate act on unverified values is planned for v5.
|
|
439
517
|
- **Docker Compose:** `${VAR}`, `${VAR:-default}`, and the no-colon `${VAR-default}` form are all supported. The `${VAR:?}`/`${VAR:+}` forms and embedded/multiple interpolation references in one value aren't evaluated yet.
|
|
440
518
|
- **Kubernetes:** `env[].valueFrom.secretKeyRef`/`configMapKeyRef` are matched by the container's environment-variable name (`env[].name`) — name your schema variable after the env var, not the key — and, when the referenced Secret/ConfigMap is defined in the same manifest, the referenced key is validated to actually exist there. `envFrom.prefix` is applied. `initContainers` are selectable via `--container` but never auto-selected by default.
|
|
441
519
|
- **Source discovery:** Python is AST-based; JavaScript/TypeScript is pattern-based. No other language is discovered yet.
|
|
442
520
|
- **.env:** multiline quoted values aren't supported.
|
|
521
|
+
- **Windows:** the write-safety fix for a failed permission change (BL-162) hasn't been run on Windows; where `os.fchmod` is missing (Windows before Python 3.13) it's skipped, which is tested on Linux by simulation only.
|
|
522
|
+
- **CI:** runs on Ubuntu only (Python 3.10–3.12).
|
|
443
523
|
|
|
444
524
|
These are bounded, tracked engineering work — see the [roadmap](ROADMAP.md) for what's next and what EnvShield deliberately isn't building.
|
|
445
525
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.8.0"
|