envshield 4.7.1__tar.gz → 4.7.4__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.1 → envshield-4.7.4}/PKG-INFO +129 -7
- {envshield-4.7.1 → envshield-4.7.4}/README.md +128 -6
- envshield-4.7.4/envshield/__init__.py +1 -0
- envshield-4.7.4/envshield/classifier/__init__.py +12 -0
- envshield-4.7.4/envshield/classifier/context_classifier.py +313 -0
- envshield-4.7.4/envshield/classifier/tokenizer.py +299 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/cli.py +88 -28
- {envshield-4.7.1 → envshield-4.7.4}/envshield/config/manager.py +124 -13
- envshield-4.7.4/envshield/core/dependency_diff.py +235 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/discovery.py +32 -13
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/doctor.py +7 -7
- envshield-4.7.4/envshield/core/enforcement.py +191 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/exceptions.py +15 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/explain.py +12 -11
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/hooks_manager.py +56 -10
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/importer.py +13 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/scanner.py +642 -10
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/schema_manager.py +48 -25
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/service_discovery.py +19 -2
- {envshield-4.7.1 → envshield-4.7.4}/envshield/parsers/_docker_compose.py +88 -11
- envshield-4.7.4/envshield/parsers/factory.py +95 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield.egg-info/PKG-INFO +129 -7
- {envshield-4.7.1 → envshield-4.7.4}/envshield.egg-info/SOURCES.txt +4 -0
- {envshield-4.7.1 → envshield-4.7.4}/pyproject.toml +1 -1
- envshield-4.7.1/envshield/__init__.py +0 -1
- envshield-4.7.1/envshield/core/dependency_diff.py +0 -112
- envshield-4.7.1/envshield/parsers/factory.py +0 -52
- {envshield-4.7.1 → envshield-4.7.4}/LICENSE.md +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/__main__.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/config/__init__.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/__init__.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/contract_diff.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/dependency_snapshot.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/file_updater.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/generator.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/inspector.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/schema_snapshot.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/schema_types.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/service_manager.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/core/setup_manager.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/parsers/__init__.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/parsers/_base.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/parsers/_deployment.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/parsers/_dotenv.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/parsers/_kubernetes.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/parsers/_python.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/state.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/utils/__init__.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/utils/git_utils.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield/utils/paths.py +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield.egg-info/dependency_links.txt +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield.egg-info/entry_points.txt +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield.egg-info/requires.txt +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/envshield.egg-info/top_level.txt +0 -0
- {envshield-4.7.1 → envshield-4.7.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: envshield
|
|
3
|
-
Version: 4.7.
|
|
3
|
+
Version: 4.7.4
|
|
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
|
|
@@ -270,7 +270,7 @@ Every command above documents its own options with `--help`; the full reference
|
|
|
270
270
|
|
|
271
271
|
## Git / CI workflow
|
|
272
272
|
|
|
273
|
-
This is where the schema stops being documentation and starts being enforcement. Three concrete things EnvShield catches
|
|
273
|
+
This is where the schema stops being documentation and starts being enforcement. Three concrete things EnvShield catches before a change reaches production:
|
|
274
274
|
|
|
275
275
|
**A new environment variable used in code but missing from the schema:**
|
|
276
276
|
|
|
@@ -284,12 +284,14 @@ envshield undeclared origin/main HEAD --service api
|
|
|
284
284
|
envshield schema diff origin/main HEAD --service api
|
|
285
285
|
```
|
|
286
286
|
|
|
287
|
-
**A secret accidentally introduced into a commit
|
|
287
|
+
**A secret accidentally introduced into a commit:**
|
|
288
288
|
|
|
289
289
|
```bash
|
|
290
290
|
envshield hook install
|
|
291
291
|
```
|
|
292
292
|
|
|
293
|
+
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.
|
|
294
|
+
|
|
293
295
|
In CI, diff against the merge-base, not the base branch's current tip (which can move while the PR is open):
|
|
294
296
|
|
|
295
297
|
```yaml
|
|
@@ -308,7 +310,25 @@ jobs:
|
|
|
308
310
|
envshield undeclared "$BASE" HEAD
|
|
309
311
|
```
|
|
310
312
|
|
|
311
|
-
Both commands exit non-zero on a real finding
|
|
313
|
+
Both commands exit non-zero on a real finding, and also on an error that prevented evaluation (a bad revision, a schema that failed to parse) — never a silent "clean" for something it couldn't actually check. Add `--json` if you want to post a custom summary instead.
|
|
314
|
+
|
|
315
|
+
### SARIF output (`undeclared --sarif`)
|
|
316
|
+
|
|
317
|
+
`envshield undeclared` also supports `--sarif` — a standard [SARIF 2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/) log, for GitHub Code Scanning or any other SARIF-consuming CI tool, so a missing declaration is annotated on its exact file and line rather than only appearing in a CI log:
|
|
318
|
+
|
|
319
|
+
```yaml
|
|
320
|
+
- run: |
|
|
321
|
+
BASE=$(git merge-base "origin/${{ github.base_ref }}" HEAD)
|
|
322
|
+
envshield undeclared "$BASE" HEAD --sarif > envshield.sarif
|
|
323
|
+
continue-on-error: true # upload the report even when it found something
|
|
324
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
325
|
+
with:
|
|
326
|
+
sarif_file: envshield.sarif
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
`--sarif` only exists for `undeclared`, deliberately: each finding there has a real file and line to point at. `schema diff` findings describe the *contract* (a variable added, removed, or changed) rather than a source location, so forcing them into SARIF would mean inventing a line number that isn't real — use its `--json`/exit-code output instead, or annotate it yourself (e.g. a `jq` step over the JSON, emitting `::error::` workflow commands).
|
|
330
|
+
|
|
331
|
+
An error that prevents evaluating a service (a bad revision, a schema that fails to parse) is never turned into a fabricated SARIF result — it's reported as a SARIF `invocation` with `executionSuccessful: false`, so a consumer can't mistake "couldn't check" for "checked and clean."
|
|
312
332
|
|
|
313
333
|
---
|
|
314
334
|
|
|
@@ -410,6 +430,34 @@ A few things worth knowing:
|
|
|
410
430
|
|
|
411
431
|
---
|
|
412
432
|
|
|
433
|
+
## Docker Compose base + override layers (`files:`)
|
|
434
|
+
|
|
435
|
+
A manifest registration normally points at one file:
|
|
436
|
+
|
|
437
|
+
```yaml
|
|
438
|
+
manifests:
|
|
439
|
+
- file: docker-compose.yml
|
|
440
|
+
containers:
|
|
441
|
+
api: api
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
A real Compose project commonly splits its configuration across a base file plus an override that Compose applies automatically (`docker-compose.yml` + `docker-compose.override.yml`, or any other `-f a -f b` combination). Registering only the base misses whatever the override adds; registering the override alone reports the base's own variables as falsely missing, since an override file is deliberately just a delta. `files:` registers the whole ordered layer set as **one** logical manifest instead:
|
|
445
|
+
|
|
446
|
+
```yaml
|
|
447
|
+
manifests:
|
|
448
|
+
- files:
|
|
449
|
+
- docker-compose.yml
|
|
450
|
+
- docker-compose.override.yml
|
|
451
|
+
containers:
|
|
452
|
+
api: api
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Order matters — later files override earlier ones, exactly like `docker compose -f docker-compose.yml -f docker-compose.override.yml` itself. `check`/`doctor`/`explain` all validate the merged result as a single source, under one combined label (`"docker-compose.yml + docker-compose.override.yml"`).
|
|
456
|
+
|
|
457
|
+
This is Compose-specific merge behavior for the fields EnvShield already understands (`environment:`, `env_file:`) — **not** a general YAML merge engine. Volumes, networks, ports, build, and every other Compose field are never inspected or merged. `files:` is unrelated to `completeness: union` above: union combines *presence across different kinds of source* (a local file and a manifest); `files:` merges *values across ordered layers of one Compose source*. A manifest entry can declare `file:` or `files:`, never both.
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
413
461
|
## Supported languages / discovery
|
|
414
462
|
|
|
415
463
|
Source-code discovery (what powers `undeclared` and `explain`'s "used in source" section) is AST-based for **Python** (`os.environ.get`, `os.getenv`, `os.environ[...]`) and pattern-based for **JavaScript/TypeScript** (`process.env.X`, `process.env["X"]`, `import.meta.env.X`, including single-level destructuring). No other language is discovered yet — the schema and validation commands work with any stack, but `undeclared`/`explain` can only see what's read from these two.
|
|
@@ -420,10 +468,84 @@ Source-code discovery (what powers `undeclared` and `explain`'s "used in source"
|
|
|
420
468
|
|
|
421
469
|
## Secret safety
|
|
422
470
|
|
|
423
|
-
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.
|
|
471
|
+
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
|
+
|
|
473
|
+
### Secret scanning
|
|
474
|
+
|
|
475
|
+
**`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.
|
|
476
|
+
|
|
477
|
+
### Context-aware classification
|
|
478
|
+
|
|
479
|
+
Python candidates are analyzed for syntactic context to distinguish code patterns from secret values:
|
|
480
|
+
|
|
481
|
+
```python
|
|
482
|
+
# Classified as code (suppressed):
|
|
483
|
+
client(key=CONFIG_CONSTANT)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def handler(api_key: str): ...
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
# Classified as likely secret (detected):
|
|
490
|
+
client(key="sk_live_abc123...")
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
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.
|
|
494
|
+
|
|
495
|
+
### Git hook enforcement
|
|
496
|
+
|
|
497
|
+
**`envshield hook install`** wires secret scanning into your Git workflow with classification-based enforcement:
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
envshield hook install
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
The pre-commit hook runs `scan --staged --enforce`, which scans only staged content and applies an enforcement policy based on classification confidence.
|
|
504
|
+
|
|
505
|
+
#### Enforcement behavior
|
|
506
|
+
|
|
507
|
+
| Finding type | Interactive (local commit) | Non-interactive (CI) |
|
|
508
|
+
|---|---|---|
|
|
509
|
+
| No findings | Allow | Allow |
|
|
510
|
+
| Clearly code-shaped | Suppressed | Suppressed |
|
|
511
|
+
| Ambiguous | Block | Block |
|
|
512
|
+
| High-confidence secret | Block + explicit override | Block |
|
|
513
|
+
|
|
514
|
+
**Clearly code-shaped** candidates (type annotations, function keyword arguments, etc.) are suppressed by the classifier and don't produce findings.
|
|
515
|
+
|
|
516
|
+
**Ambiguous** findings — insufficient context to confidently classify — block without override in both modes.
|
|
517
|
+
|
|
518
|
+
**High-confidence secrets** (string literals matching secret patterns) block by default but offer an interactive override for local commits:
|
|
519
|
+
|
|
520
|
+
```
|
|
521
|
+
⚠ EnvShield found potential secrets
|
|
522
|
+
|
|
523
|
+
HIGH CONFIDENCE
|
|
524
|
+
src/config.py:42 Generic API Key [REDACTED (30 chars)]
|
|
525
|
+
|
|
526
|
+
[1] Abort commit
|
|
527
|
+
[2] Commit anyway
|
|
528
|
+
|
|
529
|
+
Choice: 2
|
|
530
|
+
|
|
531
|
+
Type COMMIT ANYWAY to confirm: COMMIT ANYWAY
|
|
532
|
+
|
|
533
|
+
Proceeding with commit. The finding remains in the repository history.
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
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.
|
|
537
|
+
|
|
538
|
+
In CI or other non-interactive contexts, high-confidence findings block with no prompt — the commit is refused, and the scan exits non-zero.
|
|
539
|
+
|
|
540
|
+
#### Scanning modes
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
envshield scan # Audit entire codebase
|
|
544
|
+
envshield scan --staged # Scan only staged Git content
|
|
545
|
+
envshield scan --staged --enforce # Staged scan + enforcement policy
|
|
546
|
+
```
|
|
424
547
|
|
|
425
|
-
-
|
|
426
|
-
- **`envshield hook install`** wires `scan --staged` into a pre-commit hook, so a real secret is caught before it's committed, not after.
|
|
548
|
+
The pre-commit hook uses the third form. `scan` without flags is for repository-wide audits, not commit-time enforcement.
|
|
427
549
|
|
|
428
550
|
---
|
|
429
551
|
|
|
@@ -211,7 +211,7 @@ Every command above documents its own options with `--help`; the full reference
|
|
|
211
211
|
|
|
212
212
|
## Git / CI workflow
|
|
213
213
|
|
|
214
|
-
This is where the schema stops being documentation and starts being enforcement. Three concrete things EnvShield catches
|
|
214
|
+
This is where the schema stops being documentation and starts being enforcement. Three concrete things EnvShield catches before a change reaches production:
|
|
215
215
|
|
|
216
216
|
**A new environment variable used in code but missing from the schema:**
|
|
217
217
|
|
|
@@ -225,12 +225,14 @@ envshield undeclared origin/main HEAD --service api
|
|
|
225
225
|
envshield schema diff origin/main HEAD --service api
|
|
226
226
|
```
|
|
227
227
|
|
|
228
|
-
**A secret accidentally introduced into a commit
|
|
228
|
+
**A secret accidentally introduced into a commit:**
|
|
229
229
|
|
|
230
230
|
```bash
|
|
231
231
|
envshield hook install
|
|
232
232
|
```
|
|
233
233
|
|
|
234
|
+
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.
|
|
235
|
+
|
|
234
236
|
In CI, diff against the merge-base, not the base branch's current tip (which can move while the PR is open):
|
|
235
237
|
|
|
236
238
|
```yaml
|
|
@@ -249,7 +251,25 @@ jobs:
|
|
|
249
251
|
envshield undeclared "$BASE" HEAD
|
|
250
252
|
```
|
|
251
253
|
|
|
252
|
-
Both commands exit non-zero on a real finding
|
|
254
|
+
Both commands exit non-zero on a real finding, and also on an error that prevented evaluation (a bad revision, a schema that failed to parse) — never a silent "clean" for something it couldn't actually check. Add `--json` if you want to post a custom summary instead.
|
|
255
|
+
|
|
256
|
+
### SARIF output (`undeclared --sarif`)
|
|
257
|
+
|
|
258
|
+
`envshield undeclared` also supports `--sarif` — a standard [SARIF 2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/) log, for GitHub Code Scanning or any other SARIF-consuming CI tool, so a missing declaration is annotated on its exact file and line rather than only appearing in a CI log:
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
- run: |
|
|
262
|
+
BASE=$(git merge-base "origin/${{ github.base_ref }}" HEAD)
|
|
263
|
+
envshield undeclared "$BASE" HEAD --sarif > envshield.sarif
|
|
264
|
+
continue-on-error: true # upload the report even when it found something
|
|
265
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
266
|
+
with:
|
|
267
|
+
sarif_file: envshield.sarif
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`--sarif` only exists for `undeclared`, deliberately: each finding there has a real file and line to point at. `schema diff` findings describe the *contract* (a variable added, removed, or changed) rather than a source location, so forcing them into SARIF would mean inventing a line number that isn't real — use its `--json`/exit-code output instead, or annotate it yourself (e.g. a `jq` step over the JSON, emitting `::error::` workflow commands).
|
|
271
|
+
|
|
272
|
+
An error that prevents evaluating a service (a bad revision, a schema that fails to parse) is never turned into a fabricated SARIF result — it's reported as a SARIF `invocation` with `executionSuccessful: false`, so a consumer can't mistake "couldn't check" for "checked and clean."
|
|
253
273
|
|
|
254
274
|
---
|
|
255
275
|
|
|
@@ -351,6 +371,34 @@ A few things worth knowing:
|
|
|
351
371
|
|
|
352
372
|
---
|
|
353
373
|
|
|
374
|
+
## Docker Compose base + override layers (`files:`)
|
|
375
|
+
|
|
376
|
+
A manifest registration normally points at one file:
|
|
377
|
+
|
|
378
|
+
```yaml
|
|
379
|
+
manifests:
|
|
380
|
+
- file: docker-compose.yml
|
|
381
|
+
containers:
|
|
382
|
+
api: api
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
A real Compose project commonly splits its configuration across a base file plus an override that Compose applies automatically (`docker-compose.yml` + `docker-compose.override.yml`, or any other `-f a -f b` combination). Registering only the base misses whatever the override adds; registering the override alone reports the base's own variables as falsely missing, since an override file is deliberately just a delta. `files:` registers the whole ordered layer set as **one** logical manifest instead:
|
|
386
|
+
|
|
387
|
+
```yaml
|
|
388
|
+
manifests:
|
|
389
|
+
- files:
|
|
390
|
+
- docker-compose.yml
|
|
391
|
+
- docker-compose.override.yml
|
|
392
|
+
containers:
|
|
393
|
+
api: api
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Order matters — later files override earlier ones, exactly like `docker compose -f docker-compose.yml -f docker-compose.override.yml` itself. `check`/`doctor`/`explain` all validate the merged result as a single source, under one combined label (`"docker-compose.yml + docker-compose.override.yml"`).
|
|
397
|
+
|
|
398
|
+
This is Compose-specific merge behavior for the fields EnvShield already understands (`environment:`, `env_file:`) — **not** a general YAML merge engine. Volumes, networks, ports, build, and every other Compose field are never inspected or merged. `files:` is unrelated to `completeness: union` above: union combines *presence across different kinds of source* (a local file and a manifest); `files:` merges *values across ordered layers of one Compose source*. A manifest entry can declare `file:` or `files:`, never both.
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
354
402
|
## Supported languages / discovery
|
|
355
403
|
|
|
356
404
|
Source-code discovery (what powers `undeclared` and `explain`'s "used in source" section) is AST-based for **Python** (`os.environ.get`, `os.getenv`, `os.environ[...]`) and pattern-based for **JavaScript/TypeScript** (`process.env.X`, `process.env["X"]`, `import.meta.env.X`, including single-level destructuring). No other language is discovered yet — the schema and validation commands work with any stack, but `undeclared`/`explain` can only see what's read from these two.
|
|
@@ -361,10 +409,84 @@ Source-code discovery (what powers `undeclared` and `explain`'s "used in source"
|
|
|
361
409
|
|
|
362
410
|
## Secret safety
|
|
363
411
|
|
|
364
|
-
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.
|
|
412
|
+
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
|
+
|
|
414
|
+
### Secret scanning
|
|
415
|
+
|
|
416
|
+
**`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.
|
|
417
|
+
|
|
418
|
+
### Context-aware classification
|
|
419
|
+
|
|
420
|
+
Python candidates are analyzed for syntactic context to distinguish code patterns from secret values:
|
|
421
|
+
|
|
422
|
+
```python
|
|
423
|
+
# Classified as code (suppressed):
|
|
424
|
+
client(key=CONFIG_CONSTANT)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def handler(api_key: str): ...
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
# Classified as likely secret (detected):
|
|
431
|
+
client(key="sk_live_abc123...")
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
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.
|
|
435
|
+
|
|
436
|
+
### Git hook enforcement
|
|
437
|
+
|
|
438
|
+
**`envshield hook install`** wires secret scanning into your Git workflow with classification-based enforcement:
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
envshield hook install
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
The pre-commit hook runs `scan --staged --enforce`, which scans only staged content and applies an enforcement policy based on classification confidence.
|
|
445
|
+
|
|
446
|
+
#### Enforcement behavior
|
|
447
|
+
|
|
448
|
+
| Finding type | Interactive (local commit) | Non-interactive (CI) |
|
|
449
|
+
|---|---|---|
|
|
450
|
+
| No findings | Allow | Allow |
|
|
451
|
+
| Clearly code-shaped | Suppressed | Suppressed |
|
|
452
|
+
| Ambiguous | Block | Block |
|
|
453
|
+
| High-confidence secret | Block + explicit override | Block |
|
|
454
|
+
|
|
455
|
+
**Clearly code-shaped** candidates (type annotations, function keyword arguments, etc.) are suppressed by the classifier and don't produce findings.
|
|
456
|
+
|
|
457
|
+
**Ambiguous** findings — insufficient context to confidently classify — block without override in both modes.
|
|
458
|
+
|
|
459
|
+
**High-confidence secrets** (string literals matching secret patterns) block by default but offer an interactive override for local commits:
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
⚠ EnvShield found potential secrets
|
|
463
|
+
|
|
464
|
+
HIGH CONFIDENCE
|
|
465
|
+
src/config.py:42 Generic API Key [REDACTED (30 chars)]
|
|
466
|
+
|
|
467
|
+
[1] Abort commit
|
|
468
|
+
[2] Commit anyway
|
|
469
|
+
|
|
470
|
+
Choice: 2
|
|
471
|
+
|
|
472
|
+
Type COMMIT ANYWAY to confirm: COMMIT ANYWAY
|
|
473
|
+
|
|
474
|
+
Proceeding with commit. The finding remains in the repository history.
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
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.
|
|
478
|
+
|
|
479
|
+
In CI or other non-interactive contexts, high-confidence findings block with no prompt — the commit is refused, and the scan exits non-zero.
|
|
480
|
+
|
|
481
|
+
#### Scanning modes
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
envshield scan # Audit entire codebase
|
|
485
|
+
envshield scan --staged # Scan only staged Git content
|
|
486
|
+
envshield scan --staged --enforce # Staged scan + enforcement policy
|
|
487
|
+
```
|
|
365
488
|
|
|
366
|
-
-
|
|
367
|
-
- **`envshield hook install`** wires `scan --staged` into a pre-commit hook, so a real secret is caught before it's committed, not after.
|
|
489
|
+
The pre-commit hook uses the third form. `scan` without flags is for repository-wide audits, not commit-time enforcement.
|
|
368
490
|
|
|
369
491
|
---
|
|
370
492
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.7.4"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# envshield/classifier/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Context classifier for false-positive reduction in secret scanning.
|
|
4
|
+
|
|
5
|
+
This module provides the tokenizer and context classifier used to distinguish
|
|
6
|
+
code patterns (function keyword arguments, type annotations, destructuring)
|
|
7
|
+
from actual secret values, reducing false positives in the Generic API Key
|
|
8
|
+
detector.
|
|
9
|
+
|
|
10
|
+
See Milestone 2 (tokenizer) and Milestone 3 (classifier) documentation for
|
|
11
|
+
design rationale and limitations.
|
|
12
|
+
"""
|