code-secret-handler 1.0.0
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.
- package/LICENSE +79 -0
- package/README.md +221 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +121 -0
- package/dist/cli.js.map +1 -0
- package/dist/env/envWriter.d.ts +9 -0
- package/dist/env/envWriter.d.ts.map +1 -0
- package/dist/env/envWriter.js +76 -0
- package/dist/env/envWriter.js.map +1 -0
- package/dist/env/keyNamer.d.ts +13 -0
- package/dist/env/keyNamer.d.ts.map +1 -0
- package/dist/env/keyNamer.js +33 -0
- package/dist/env/keyNamer.js.map +1 -0
- package/dist/interactive/confirm.d.ts +16 -0
- package/dist/interactive/confirm.d.ts.map +1 -0
- package/dist/interactive/confirm.js +72 -0
- package/dist/interactive/confirm.js.map +1 -0
- package/dist/license/gitContext.d.ts +16 -0
- package/dist/license/gitContext.d.ts.map +1 -0
- package/dist/license/gitContext.js +117 -0
- package/dist/license/gitContext.js.map +1 -0
- package/dist/license/licenseCheck.d.ts +22 -0
- package/dist/license/licenseCheck.d.ts.map +1 -0
- package/dist/license/licenseCheck.js +119 -0
- package/dist/license/licenseCheck.js.map +1 -0
- package/dist/license/telemetry.d.ts +17 -0
- package/dist/license/telemetry.d.ts.map +1 -0
- package/dist/license/telemetry.js +87 -0
- package/dist/license/telemetry.js.map +1 -0
- package/dist/reporter/console.d.ts +3 -0
- package/dist/reporter/console.d.ts.map +1 -0
- package/dist/reporter/console.js +96 -0
- package/dist/reporter/console.js.map +1 -0
- package/dist/reporter/csv.d.ts +6 -0
- package/dist/reporter/csv.d.ts.map +1 -0
- package/dist/reporter/csv.js +56 -0
- package/dist/reporter/csv.js.map +1 -0
- package/dist/reporter/index.d.ts +13 -0
- package/dist/reporter/index.d.ts.map +1 -0
- package/dist/reporter/index.js +52 -0
- package/dist/reporter/index.js.map +1 -0
- package/dist/reporter/json.d.ts +7 -0
- package/dist/reporter/json.d.ts.map +1 -0
- package/dist/reporter/json.js +39 -0
- package/dist/reporter/json.js.map +1 -0
- package/dist/reporter/markdown.d.ts +6 -0
- package/dist/reporter/markdown.d.ts.map +1 -0
- package/dist/reporter/markdown.js +106 -0
- package/dist/reporter/markdown.js.map +1 -0
- package/dist/rewriter/index.d.ts +9 -0
- package/dist/rewriter/index.d.ts.map +1 -0
- package/dist/rewriter/index.js +95 -0
- package/dist/rewriter/index.js.map +1 -0
- package/dist/rewriter/langDetector.d.ts +7 -0
- package/dist/rewriter/langDetector.d.ts.map +1 -0
- package/dist/rewriter/langDetector.js +96 -0
- package/dist/rewriter/langDetector.js.map +1 -0
- package/dist/rewriter/templates/csharp.d.ts +8 -0
- package/dist/rewriter/templates/csharp.d.ts.map +1 -0
- package/dist/rewriter/templates/csharp.js +36 -0
- package/dist/rewriter/templates/csharp.js.map +1 -0
- package/dist/rewriter/templates/generic.d.ts +7 -0
- package/dist/rewriter/templates/generic.d.ts.map +1 -0
- package/dist/rewriter/templates/generic.js +40 -0
- package/dist/rewriter/templates/generic.js.map +1 -0
- package/dist/rewriter/templates/go.d.ts +8 -0
- package/dist/rewriter/templates/go.d.ts.map +1 -0
- package/dist/rewriter/templates/go.js +56 -0
- package/dist/rewriter/templates/go.js.map +1 -0
- package/dist/rewriter/templates/java.d.ts +7 -0
- package/dist/rewriter/templates/java.d.ts.map +1 -0
- package/dist/rewriter/templates/java.js +31 -0
- package/dist/rewriter/templates/java.js.map +1 -0
- package/dist/rewriter/templates/javascript.d.ts +10 -0
- package/dist/rewriter/templates/javascript.d.ts.map +1 -0
- package/dist/rewriter/templates/javascript.js +54 -0
- package/dist/rewriter/templates/javascript.js.map +1 -0
- package/dist/rewriter/templates/php.d.ts +8 -0
- package/dist/rewriter/templates/php.d.ts.map +1 -0
- package/dist/rewriter/templates/php.js +41 -0
- package/dist/rewriter/templates/php.js.map +1 -0
- package/dist/rewriter/templates/properties.d.ts +9 -0
- package/dist/rewriter/templates/properties.d.ts.map +1 -0
- package/dist/rewriter/templates/properties.js +39 -0
- package/dist/rewriter/templates/properties.js.map +1 -0
- package/dist/rewriter/templates/python.d.ts +8 -0
- package/dist/rewriter/templates/python.d.ts.map +1 -0
- package/dist/rewriter/templates/python.js +48 -0
- package/dist/rewriter/templates/python.js.map +1 -0
- package/dist/rewriter/templates/ruby.d.ts +8 -0
- package/dist/rewriter/templates/ruby.d.ts.map +1 -0
- package/dist/rewriter/templates/ruby.js +40 -0
- package/dist/rewriter/templates/ruby.js.map +1 -0
- package/dist/rewriter/templates/rust.d.ts +7 -0
- package/dist/rewriter/templates/rust.d.ts.map +1 -0
- package/dist/rewriter/templates/rust.js +27 -0
- package/dist/rewriter/templates/rust.js.map +1 -0
- package/dist/rewriter/templates/shell.d.ts +7 -0
- package/dist/rewriter/templates/shell.d.ts.map +1 -0
- package/dist/rewriter/templates/shell.js +42 -0
- package/dist/rewriter/templates/shell.js.map +1 -0
- package/dist/rewriter/templates/toml.d.ts +9 -0
- package/dist/rewriter/templates/toml.d.ts.map +1 -0
- package/dist/rewriter/templates/toml.js +34 -0
- package/dist/rewriter/templates/toml.js.map +1 -0
- package/dist/rewriter/templates/yaml.d.ts +8 -0
- package/dist/rewriter/templates/yaml.d.ts.map +1 -0
- package/dist/rewriter/templates/yaml.js +37 -0
- package/dist/rewriter/templates/yaml.js.map +1 -0
- package/dist/scanner/detector.d.ts +18 -0
- package/dist/scanner/detector.d.ts.map +1 -0
- package/dist/scanner/detector.js +156 -0
- package/dist/scanner/detector.js.map +1 -0
- package/dist/scanner/fileWalker.d.ts +6 -0
- package/dist/scanner/fileWalker.d.ts.map +1 -0
- package/dist/scanner/fileWalker.js +146 -0
- package/dist/scanner/fileWalker.js.map +1 -0
- package/dist/scanner/index.d.ts +8 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +74 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/rules/aws.d.ts +6 -0
- package/dist/scanner/rules/aws.d.ts.map +1 -0
- package/dist/scanner/rules/aws.js +90 -0
- package/dist/scanner/rules/aws.js.map +1 -0
- package/dist/scanner/rules/azure.d.ts +6 -0
- package/dist/scanner/rules/azure.d.ts.map +1 -0
- package/dist/scanner/rules/azure.js +86 -0
- package/dist/scanner/rules/azure.js.map +1 -0
- package/dist/scanner/rules/cloud.d.ts +11 -0
- package/dist/scanner/rules/cloud.d.ts.map +1 -0
- package/dist/scanner/rules/cloud.js +1066 -0
- package/dist/scanner/rules/cloud.js.map +1 -0
- package/dist/scanner/rules/database.d.ts +6 -0
- package/dist/scanner/rules/database.d.ts.map +1 -0
- package/dist/scanner/rules/database.js +283 -0
- package/dist/scanner/rules/database.js.map +1 -0
- package/dist/scanner/rules/gcp.d.ts +6 -0
- package/dist/scanner/rules/gcp.d.ts.map +1 -0
- package/dist/scanner/rules/gcp.js +108 -0
- package/dist/scanner/rules/gcp.js.map +1 -0
- package/dist/scanner/rules/generic.d.ts +13 -0
- package/dist/scanner/rules/generic.d.ts.map +1 -0
- package/dist/scanner/rules/generic.js +305 -0
- package/dist/scanner/rules/generic.js.map +1 -0
- package/dist/scanner/rules/index.d.ts +18 -0
- package/dist/scanner/rules/index.d.ts.map +1 -0
- package/dist/scanner/rules/index.js +40 -0
- package/dist/scanner/rules/index.js.map +1 -0
- package/dist/scanner/rules/jwt.d.ts +6 -0
- package/dist/scanner/rules/jwt.d.ts.map +1 -0
- package/dist/scanner/rules/jwt.js +97 -0
- package/dist/scanner/rules/jwt.js.map +1 -0
- package/dist/scanner/rules/network.d.ts +7 -0
- package/dist/scanner/rules/network.d.ts.map +1 -0
- package/dist/scanner/rules/network.js +166 -0
- package/dist/scanner/rules/network.js.map +1 -0
- package/dist/scanner/rules/privateKey.d.ts +6 -0
- package/dist/scanner/rules/privateKey.d.ts.map +1 -0
- package/dist/scanner/rules/privateKey.js +60 -0
- package/dist/scanner/rules/privateKey.js.map +1 -0
- package/dist/scanner/rules/ssh.d.ts +6 -0
- package/dist/scanner/rules/ssh.d.ts.map +1 -0
- package/dist/scanner/rules/ssh.js +49 -0
- package/dist/scanner/rules/ssh.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Copyright (c) 2024 Vic (vic.grambo@gmail.com)
|
|
2
|
+
|
|
3
|
+
PolyForm Noncommercial License 1.0.0
|
|
4
|
+
|
|
5
|
+
Acceptance
|
|
6
|
+
|
|
7
|
+
In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
|
|
8
|
+
|
|
9
|
+
Use Grant
|
|
10
|
+
|
|
11
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case for your personal and noncommercial purposes only.
|
|
12
|
+
|
|
13
|
+
Noncommercial Purposes
|
|
14
|
+
|
|
15
|
+
Any of the following uses of the software counts as a noncommercial purpose:
|
|
16
|
+
|
|
17
|
+
* Personal research, study, learning, private entertainment, or hobby projects without any anticipated commercial application.
|
|
18
|
+
* Use by a charitable organization, educational institution, public research organization, public safety or health organization, environmental protection organization, or government institution.
|
|
19
|
+
|
|
20
|
+
Commercial Use Reserved
|
|
21
|
+
|
|
22
|
+
Any use of the software to directly or indirectly generate revenue, reduce costs, or obtain a commercial advantage for a business, company, organization, or for-profit entity requires a separate commercial license granted in writing by the licensor.
|
|
23
|
+
|
|
24
|
+
This includes, but is not limited to:
|
|
25
|
+
* Use within a company, startup, agency, or any for-profit organization.
|
|
26
|
+
* Running the tool as part of an automated CI/CD pipeline in a commercial context.
|
|
27
|
+
* Offering services built on or using this tool to paying customers.
|
|
28
|
+
* Any use by a GitHub Organization, GitLab Group, or equivalent entity.
|
|
29
|
+
|
|
30
|
+
Commercial Licensing
|
|
31
|
+
|
|
32
|
+
To obtain a commercial license, please contact:
|
|
33
|
+
|
|
34
|
+
Vic — vic.grambo@gmail.com
|
|
35
|
+
|
|
36
|
+
Commercial licensees will receive a license key to be set as:
|
|
37
|
+
CSH_LICENSE_KEY=<your-key>
|
|
38
|
+
|
|
39
|
+
Notices
|
|
40
|
+
|
|
41
|
+
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with "Required Notice:" that the licensor provides with the software.
|
|
42
|
+
|
|
43
|
+
No Other Rights
|
|
44
|
+
|
|
45
|
+
These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else.
|
|
46
|
+
|
|
47
|
+
Patent Defense
|
|
48
|
+
|
|
49
|
+
If you make any written claim that the software infringes or contributes to infringement of any patent, your license for the software granted under these terms ends immediately. If your company makes such a claim, your license ends immediately for work on behalf of your company.
|
|
50
|
+
|
|
51
|
+
Violations
|
|
52
|
+
|
|
53
|
+
The first time you are notified in writing that you have violated any of these terms, your license can nonetheless continue if you come into full compliance with these terms within 30 days of receiving that notice. If any violation of these terms is willful, the 30-day cure period above does not apply.
|
|
54
|
+
|
|
55
|
+
No Liability
|
|
56
|
+
|
|
57
|
+
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
|
|
58
|
+
|
|
59
|
+
Telemetry Disclosure
|
|
60
|
+
|
|
61
|
+
This software collects anonymous usage telemetry to assist with license compliance monitoring. The following data is collected when the tool is run:
|
|
62
|
+
|
|
63
|
+
* Tool version, operating system, and Node.js version
|
|
64
|
+
* Git remote URL, repository owner, and repository name
|
|
65
|
+
* Git platform (GitHub, GitLab, Bitbucket, or other)
|
|
66
|
+
* Whether the repository owner is an Organization or personal User
|
|
67
|
+
* Whether a commercial license key was provided
|
|
68
|
+
* Timestamp of the run
|
|
69
|
+
|
|
70
|
+
No source code, file contents, secret values, or personally identifiable information is ever collected.
|
|
71
|
+
|
|
72
|
+
To opt out of telemetry collection, set the following environment variable:
|
|
73
|
+
CSH_TELEMETRY=0
|
|
74
|
+
|
|
75
|
+
Definitions
|
|
76
|
+
|
|
77
|
+
"The licensor" refers to Vic (vic.grambo@gmail.com), the individual who makes the software available under these terms.
|
|
78
|
+
|
|
79
|
+
"The software" refers to the code-secret-handler CLI tool and all associated source code, binaries, and documentation made available under this license.
|
package/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# 🔐 Code Secret Handler
|
|
2
|
+
|
|
3
|
+
A **language-agnostic CLI tool** that scans any codebase for hardcoded secrets, lists them with full context, moves them to a `.env` file, and rewrites source files with the correct environment variable accessor for each language.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🔍 **Deep scanning** — 179+ secret types detected across 10 rule categories (AWS, GCP, Azure, GitHub, Stripe, Twilio, OpenAI, Anthropic, Discord, Telegram, Supabase, JWT, SSH keys, database URIs, SMTP, and many more)
|
|
10
|
+
- 🌐 **Language-aware rewriting** — auto-detects file language and uses the correct env-var syntax:
|
|
11
|
+
- `process.env.KEY` (JavaScript/TypeScript)
|
|
12
|
+
- `os.environ.get('KEY')` (Python)
|
|
13
|
+
- `System.getenv("KEY")` (Java/Kotlin)
|
|
14
|
+
- `os.Getenv("KEY")` (Go)
|
|
15
|
+
- `ENV['KEY']` (Ruby)
|
|
16
|
+
- `getenv('KEY')` (PHP)
|
|
17
|
+
- `Environment.GetEnvironmentVariable("KEY")` (C#)
|
|
18
|
+
- `std::env::var("KEY")` (Rust)
|
|
19
|
+
- `$KEY` / `$env:KEY` (Shell/PowerShell)
|
|
20
|
+
- `${KEY}` (YAML, TOML, properties files)
|
|
21
|
+
- 📋 **Multiple report formats** — console (colored), JSON, CSV, Markdown
|
|
22
|
+
- 🔒 **Collision-safe** — appends `_2`, `_3`, etc. for duplicate env key names
|
|
23
|
+
- 🧠 **Respects `.gitignore`** — only scans tracked files, skips `node_modules`, `dist`, etc.
|
|
24
|
+
- 🛡️ **Interactive** — asks before making any changes, with backup option
|
|
25
|
+
- 🔍 **Dry-run mode** — scan and report without modifying anything
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Run directly with npx (no install needed)
|
|
33
|
+
npx code-secret-handler ./my-project
|
|
34
|
+
|
|
35
|
+
# Or install globally
|
|
36
|
+
npm install -g code-secret-handler
|
|
37
|
+
csh ./my-project
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
csh [target-dir] [options]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Options
|
|
49
|
+
|
|
50
|
+
| Flag | Description | Default |
|
|
51
|
+
|------|-------------|---------|
|
|
52
|
+
| `[target-dir]` | Directory to scan | `.` (current dir) |
|
|
53
|
+
| `-r, --report <format>` | Report format: `console` \| `json` \| `csv` \| `markdown` | `console` |
|
|
54
|
+
| `-o, --output <file>` | Report output file path | Auto-derived |
|
|
55
|
+
| `-e, --env-file <path>` | Target `.env` file path | `./.env` |
|
|
56
|
+
| `--dry-run` | Scan only — no file modifications | `false` |
|
|
57
|
+
| `--no-rewrite` | Write `.env` but skip source-file rewriting | `false` |
|
|
58
|
+
| `-v, --version` | Show version | |
|
|
59
|
+
| `-h, --help` | Show help | |
|
|
60
|
+
|
|
61
|
+
### Examples
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Scan current directory (interactive)
|
|
65
|
+
csh .
|
|
66
|
+
|
|
67
|
+
# Dry run — see what would be found without changes
|
|
68
|
+
csh ./my-app --dry-run
|
|
69
|
+
|
|
70
|
+
# Write Markdown report to a file
|
|
71
|
+
csh ./my-app -r markdown -o security-report.md
|
|
72
|
+
|
|
73
|
+
# Use a custom .env file location
|
|
74
|
+
csh ./my-app -e config/.env
|
|
75
|
+
|
|
76
|
+
# Write .env but do NOT rewrite source files
|
|
77
|
+
csh ./my-app --no-rewrite
|
|
78
|
+
|
|
79
|
+
# JSON report
|
|
80
|
+
csh ./my-app -r json -o findings.json
|
|
81
|
+
|
|
82
|
+
# CSV report for spreadsheet import
|
|
83
|
+
csh ./my-app -r csv
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## How It Works
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
CLI args
|
|
92
|
+
└─► File Walker (respects .gitignore)
|
|
93
|
+
└─► Detector (179+ regex rules, line-by-line)
|
|
94
|
+
└─► Findings { file, line, col, type, rawValue, envKey }
|
|
95
|
+
├─► Reporter (console/json/csv/markdown)
|
|
96
|
+
└─► [Interactive confirmation]
|
|
97
|
+
├─► Env Writer → .env
|
|
98
|
+
└─► Rewriter → patched source files
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Detection Rules
|
|
102
|
+
|
|
103
|
+
| Category | Rules | Examples |
|
|
104
|
+
|----------|-------|----------|
|
|
105
|
+
| AWS | 10 | Access Key ID (`AKIA…`), Secret Key, Session Token, ARN, S3 bucket, MWS key, CloudFront |
|
|
106
|
+
| GCP | 11 | API Key (`AIza…`), OAuth Client, Service Account, Maps, reCAPTCHA, Firebase DB URL |
|
|
107
|
+
| Azure | 8 | Storage connection strings, SAS tokens, Service Bus, CosmosDB, Client/Tenant ID |
|
|
108
|
+
| GitHub | 4 | `ghp_`, `gho_`, `ghs_`, `github_pat_` tokens |
|
|
109
|
+
| GitLab | 2 | `glpat-` Personal Access Tokens, Runner registration tokens |
|
|
110
|
+
| Stripe | 3 | `sk_live_`, `sk_test_`, `rk_live_`, `whsec_` webhook secrets |
|
|
111
|
+
| Twilio | 3 | Account SID, Auth Token, API Key (`SK…`) |
|
|
112
|
+
| Slack | 3 | `xoxb-`/`xoxp-` tokens, webhook URLs, signing secrets |
|
|
113
|
+
| JWT / Auth | 11 | JWT secrets, session, NextAuth, Flask, Rails `secret_key_base`, CSRF, OAuth2 |
|
|
114
|
+
| SSH / PEM | 10 | RSA, EC, DSA, OpenSSH, PGP private key blocks, PKCS12 |
|
|
115
|
+
| Database | 19 | PostgreSQL, MySQL, MongoDB, Redis, MSSQL, Elasticsearch, RabbitMQ, Cassandra, Oracle, CockroachDB, Neo4j, InfluxDB, Kafka |
|
|
116
|
+
| Network | 12 | SMTP, proxy URLs, LDAP, CI/CD deploy tokens, Apple APNs, webhook URLs |
|
|
117
|
+
| AI / ML | 6 | OpenAI (`sk-`), Anthropic (`sk-ant-`), Hugging Face (`hf_`), Pinecone, Cohere, Replicate (`r8_`) |
|
|
118
|
+
| Payments | 5 | Square (`EAA…`), Razorpay, Braintree, Plaid, PayPal |
|
|
119
|
+
| SaaS | 8 | Notion (`secret_`), Linear (`lin_api_`), Airtable, HubSpot, Zendesk, Intercom, Jira, PagerDuty |
|
|
120
|
+
| DevOps | 7 | Vercel, DigitalOcean, Terraform Cloud, HashiCorp Vault, Confluent, Docker Hub, Heroku |
|
|
121
|
+
| Analytics | 8 | Amplitude, Mixpanel, Segment, New Relic (`NRAK-`), Datadog, Dynatrace, Grafana, Sentry |
|
|
122
|
+
| Generic | 21 | API keys/secrets, passwords, bearer tokens, HMAC keys, webhook secrets, license keys, connection strings |
|
|
123
|
+
| Other | 16 | Cloudflare, Cloudinary, Algolia, Mapbox, Shopify, Auth0, Okta, Firebase, LaunchDarkly, Discord, Telegram, Mailgun, Mailchimp, SendGrid, NPM, Pusher |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## After Running
|
|
128
|
+
|
|
129
|
+
1. **Verify** the `.env` file was created with the correct values.
|
|
130
|
+
2. **Add `.env` to `.gitignore`** — the tool reminds you, but please double-check!
|
|
131
|
+
3. **Rotate secrets** that may have been committed to Git history previously.
|
|
132
|
+
4. **Review** modified source files before committing.
|
|
133
|
+
5. **Share `.env.example`** (with placeholder values) with your team.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Development
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone <repo>
|
|
141
|
+
cd code-secret-handler
|
|
142
|
+
npm install
|
|
143
|
+
|
|
144
|
+
# Run in dev mode
|
|
145
|
+
npm run dev -- ./path/to/scan
|
|
146
|
+
|
|
147
|
+
# Build
|
|
148
|
+
npm run build
|
|
149
|
+
|
|
150
|
+
# Type-check
|
|
151
|
+
npm run lint
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Telemetry
|
|
157
|
+
|
|
158
|
+
`code-secret-handler` collects **anonymous usage telemetry** to help the author monitor license compliance and understand how the tool is being used.
|
|
159
|
+
|
|
160
|
+
### What is collected
|
|
161
|
+
|
|
162
|
+
| Data | Example |
|
|
163
|
+
|------|---------|
|
|
164
|
+
| Tool version | `1.0.0` |
|
|
165
|
+
| OS platform | `linux`, `darwin`, `win32` |
|
|
166
|
+
| Node.js version | `v20.11.0` |
|
|
167
|
+
| Git remote URL | `https://github.com/owner/repo` |
|
|
168
|
+
| Repo owner & name | `owner`, `repo` |
|
|
169
|
+
| Git platform | `github`, `gitlab`, `bitbucket` |
|
|
170
|
+
| Account type | `Organization` or `User` |
|
|
171
|
+
| License key provided | `true` / `false` |
|
|
172
|
+
| Timestamp | ISO 8601 |
|
|
173
|
+
|
|
174
|
+
### What is NOT collected
|
|
175
|
+
|
|
176
|
+
- ❌ Source code or file contents
|
|
177
|
+
- ❌ Secret values or credentials
|
|
178
|
+
- ❌ Personal identifiable information
|
|
179
|
+
- ❌ Anything beyond the fields listed above
|
|
180
|
+
|
|
181
|
+
### Opt out
|
|
182
|
+
|
|
183
|
+
Set `CSH_TELEMETRY=0` in your environment to disable all telemetry:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
CSH_TELEMETRY=0 csh ./my-project
|
|
187
|
+
# or permanently in your shell profile:
|
|
188
|
+
export CSH_TELEMETRY=0
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
**PolyForm Noncommercial License 1.0.0**
|
|
196
|
+
Copyright (c) 2024 Vic — vic.grambo@gmail.com
|
|
197
|
+
|
|
198
|
+
### ✅ Free for
|
|
199
|
+
|
|
200
|
+
- Individual developers for personal projects, learning, research, or hobby use
|
|
201
|
+
- Non-profit organizations, educational institutions, public research organizations, and government institutions
|
|
202
|
+
|
|
203
|
+
### ❌ Not free for
|
|
204
|
+
|
|
205
|
+
- Companies, startups, agencies, or any for-profit organization
|
|
206
|
+
- Any use that directly or indirectly generates revenue or commercial advantage
|
|
207
|
+
- Running the tool within a commercial CI/CD pipeline or product
|
|
208
|
+
|
|
209
|
+
### 💼 Commercial licensing
|
|
210
|
+
|
|
211
|
+
If you represent an organization or wish to use this tool commercially, please contact:
|
|
212
|
+
|
|
213
|
+
**vic.grambo@gmail.com**
|
|
214
|
+
|
|
215
|
+
Commercial licensees receive a license key. Set it as an environment variable before running the tool:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
CSH_LICENSE_KEY=<your-key> csh ./my-project
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The full license text is available in the [LICENSE](./LICENSE) file.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const scanner_1 = require("./scanner");
|
|
12
|
+
const envWriter_1 = require("./env/envWriter");
|
|
13
|
+
const rewriter_1 = require("./rewriter");
|
|
14
|
+
const reporter_1 = require("./reporter");
|
|
15
|
+
const confirm_1 = require("./interactive/confirm");
|
|
16
|
+
const gitContext_1 = require("./license/gitContext");
|
|
17
|
+
const licenseCheck_1 = require("./license/licenseCheck");
|
|
18
|
+
const telemetry_1 = require("./license/telemetry");
|
|
19
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
20
|
+
const program = new commander_1.Command();
|
|
21
|
+
program
|
|
22
|
+
.name('code-secret-handler')
|
|
23
|
+
.alias('csh')
|
|
24
|
+
.description(chalk_1.default.cyan('🔐 Scans your codebase for hardcoded secrets, moves them to .env, and rewrites source files.'))
|
|
25
|
+
.version(pkg.version, '-v, --version', 'Output the current version')
|
|
26
|
+
.argument('[target-dir]', 'Directory to scan (default: current directory)', '.')
|
|
27
|
+
.option('-r, --report <format>', 'Report format: console | json | csv | markdown', 'console')
|
|
28
|
+
.option('-o, --output <file>', 'Report output file path (auto-derived if omitted)')
|
|
29
|
+
.option('-e, --env-file <path>', 'Target .env file path', '.env')
|
|
30
|
+
.option('--dry-run', 'Scan and report only — do not write any files', false)
|
|
31
|
+
.option('--no-rewrite', 'Write .env but skip source-file rewriting')
|
|
32
|
+
.addHelpText('after', `
|
|
33
|
+
${chalk_1.default.bold('Examples:')}
|
|
34
|
+
${chalk_1.default.cyan('$ csh .')} Scan current directory
|
|
35
|
+
${chalk_1.default.cyan('$ csh ./my-app --dry-run')} Scan without modifying files
|
|
36
|
+
${chalk_1.default.cyan('$ csh ./my-app -r markdown -o report.md')} Scan and write Markdown report
|
|
37
|
+
${chalk_1.default.cyan('$ csh ./my-app -e secrets/.env')} Use a custom .env path
|
|
38
|
+
${chalk_1.default.cyan('$ csh ./my-app --no-rewrite')} Write .env only, skip source rewrites
|
|
39
|
+
`);
|
|
40
|
+
program.action(async (targetDirArg, opts) => {
|
|
41
|
+
console.log(chalk_1.default.bold.cyan('\n🔐 Code Secret Handler\n'));
|
|
42
|
+
const targetDir = path_1.default.resolve(targetDirArg || '.');
|
|
43
|
+
// ── License & telemetry (runs before anything else) ──────────────────────
|
|
44
|
+
const gitCtx = await (0, gitContext_1.getGitContext)(targetDir);
|
|
45
|
+
const { licenseKeyProvided } = await (0, licenseCheck_1.checkLicense)(gitCtx);
|
|
46
|
+
(0, telemetry_1.sendTelemetry)(gitCtx, licenseKeyProvided);
|
|
47
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
48
|
+
const report = (opts['report'] || 'console').toLowerCase();
|
|
49
|
+
const envFile = path_1.default.resolve(targetDir, opts['envFile'] || '.env');
|
|
50
|
+
const dryRun = Boolean(opts['dryRun']);
|
|
51
|
+
const noRewrite = !Boolean(opts['rewrite']);
|
|
52
|
+
const outputArg = opts['output'];
|
|
53
|
+
const outputPath = outputArg
|
|
54
|
+
? path_1.default.resolve(outputArg)
|
|
55
|
+
: report !== 'console'
|
|
56
|
+
? (0, reporter_1.defaultOutputPath)(report, targetDir)
|
|
57
|
+
: undefined;
|
|
58
|
+
const _options = { targetDir, report, output: outputPath, envFile, dryRun, noRewrite };
|
|
59
|
+
if (!fs_1.default.existsSync(targetDir) || !fs_1.default.statSync(targetDir).isDirectory()) {
|
|
60
|
+
console.error(chalk_1.default.red(`❌ Target directory not found: ${targetDir}`));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const validFormats = ['console', 'json', 'csv', 'markdown'];
|
|
64
|
+
if (!validFormats.includes(report)) {
|
|
65
|
+
console.error(chalk_1.default.red(`❌ Invalid report format: "${report}". Choose from: ${validFormats.join(', ')}`));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
let summary = await (0, scanner_1.runScanner)(targetDir, envFile);
|
|
69
|
+
summary.dryRun = dryRun;
|
|
70
|
+
if (outputPath)
|
|
71
|
+
summary.reportFile = outputPath;
|
|
72
|
+
const reportFile = (0, reporter_1.runReporter)(report, summary, outputPath);
|
|
73
|
+
if (reportFile) {
|
|
74
|
+
console.log(chalk_1.default.green(`\n 📄 Report written to: ${chalk_1.default.underline(reportFile)}\n`));
|
|
75
|
+
}
|
|
76
|
+
if (summary.totalFindings === 0)
|
|
77
|
+
process.exit(0);
|
|
78
|
+
if (dryRun) {
|
|
79
|
+
console.log(chalk_1.default.yellow(' ℹ️ Dry run mode — no changes applied.\n'));
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
const approved = await (0, confirm_1.confirmChanges)(summary, noRewrite);
|
|
83
|
+
if (!approved) {
|
|
84
|
+
console.log(chalk_1.default.yellow('\n ❌ Aborted — no changes made.\n'));
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
let backupChoice = 'skip';
|
|
88
|
+
if (!noRewrite && summary.totalFindings > 0) {
|
|
89
|
+
backupChoice = await (0, confirm_1.askBackupPreference)();
|
|
90
|
+
if (backupChoice === 'abort') {
|
|
91
|
+
console.log(chalk_1.default.yellow('\n ❌ Aborted — no changes made.\n'));
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const written = (0, envWriter_1.writeEnvFile)(envFile, summary.findings);
|
|
96
|
+
console.log(chalk_1.default.green(`\n ✅ ${written} secret(s) written to ${chalk_1.default.underline(envFile)}`));
|
|
97
|
+
if (!noRewrite) {
|
|
98
|
+
const results = (0, rewriter_1.rewriteFiles)(summary.findings, false);
|
|
99
|
+
let totalRewrites = 0;
|
|
100
|
+
for (const result of results) {
|
|
101
|
+
if (result.replacements === 0)
|
|
102
|
+
continue;
|
|
103
|
+
if (backupChoice === 'backup') {
|
|
104
|
+
fs_1.default.writeFileSync(`${result.filePath}.bak`, result.originalContent, 'utf-8');
|
|
105
|
+
}
|
|
106
|
+
totalRewrites += result.replacements;
|
|
107
|
+
const relPath = path_1.default.relative(targetDir, result.filePath);
|
|
108
|
+
console.log(chalk_1.default.cyan(` ✏️ ${relPath}`) +
|
|
109
|
+
chalk_1.default.gray(` — ${result.replacements} replacement(s)`) +
|
|
110
|
+
(result.dotenvInjected ? chalk_1.default.yellow(' + dotenv injected') : ''));
|
|
111
|
+
}
|
|
112
|
+
console.log(chalk_1.default.green(`\n ✅ ${totalRewrites} replacement(s) across ${results.filter(r => r.replacements > 0).length} file(s)`));
|
|
113
|
+
}
|
|
114
|
+
console.log(chalk_1.default.bold.yellow('\n ⚠️ IMPORTANT:'));
|
|
115
|
+
console.log(chalk_1.default.yellow(' 1. Add .env to your .gitignore if not already done.'));
|
|
116
|
+
console.log(chalk_1.default.yellow(' 2. Rotate any secrets that may have been committed to Git history.'));
|
|
117
|
+
console.log(chalk_1.default.yellow(' 3. Review all modified source files before committing.\n'));
|
|
118
|
+
process.exit(0);
|
|
119
|
+
});
|
|
120
|
+
program.parse(process.argv);
|
|
121
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Finding } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Appends or creates a .env file with the given findings.
|
|
4
|
+
* Keys that already exist in the file are skipped (deduplication was done earlier).
|
|
5
|
+
*
|
|
6
|
+
* @returns number of entries written
|
|
7
|
+
*/
|
|
8
|
+
export declare function writeEnvFile(envFilePath: string, findings: Finding[]): number;
|
|
9
|
+
//# sourceMappingURL=envWriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envWriter.d.ts","sourceRoot":"","sources":["../../src/env/envWriter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAsBnC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CA6C7E"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeEnvFile = writeEnvFile;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
/**
|
|
10
|
+
* Parses an existing .env file into a map of key → value.
|
|
11
|
+
*/
|
|
12
|
+
function parseEnvFile(filePath) {
|
|
13
|
+
const map = new Map();
|
|
14
|
+
if (!fs_1.default.existsSync(filePath))
|
|
15
|
+
return map;
|
|
16
|
+
const lines = fs_1.default.readFileSync(filePath, 'utf-8').split('\n');
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
const trimmed = line.trim();
|
|
19
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
20
|
+
continue;
|
|
21
|
+
const eqIdx = trimmed.indexOf('=');
|
|
22
|
+
if (eqIdx < 1)
|
|
23
|
+
continue;
|
|
24
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
25
|
+
const value = trimmed.slice(eqIdx + 1).trim().replace(/^['"`]|['"`]$/g, '');
|
|
26
|
+
map.set(key, value);
|
|
27
|
+
}
|
|
28
|
+
return map;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Appends or creates a .env file with the given findings.
|
|
32
|
+
* Keys that already exist in the file are skipped (deduplication was done earlier).
|
|
33
|
+
*
|
|
34
|
+
* @returns number of entries written
|
|
35
|
+
*/
|
|
36
|
+
function writeEnvFile(envFilePath, findings) {
|
|
37
|
+
const absPath = path_1.default.resolve(envFilePath);
|
|
38
|
+
// Ensure parent dir exists
|
|
39
|
+
const dir = path_1.default.dirname(absPath);
|
|
40
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
41
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
const existing = parseEnvFile(absPath);
|
|
44
|
+
const lines = [];
|
|
45
|
+
let written = 0;
|
|
46
|
+
// Group findings by source file for a readable header
|
|
47
|
+
const byFile = new Map();
|
|
48
|
+
for (const f of findings) {
|
|
49
|
+
if (!byFile.has(f.file))
|
|
50
|
+
byFile.set(f.file, []);
|
|
51
|
+
byFile.get(f.file).push(f);
|
|
52
|
+
}
|
|
53
|
+
const header = `\n# ─── Added by code-secret-handler on ${new Date().toISOString()} ───\n`;
|
|
54
|
+
lines.push(header);
|
|
55
|
+
for (const [file, fileFindings] of byFile) {
|
|
56
|
+
lines.push(`# Source: ${file}`);
|
|
57
|
+
for (const finding of fileFindings) {
|
|
58
|
+
if (existing.has(finding.envKey)) {
|
|
59
|
+
// Should not happen due to dedup, but be safe
|
|
60
|
+
finding.alreadyInEnv = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
// Escape value: wrap in double quotes, escape embedded double quotes
|
|
64
|
+
const safeValue = finding.rawValue.replace(/"/g, '\\"');
|
|
65
|
+
lines.push(`${finding.envKey}="${safeValue}"`);
|
|
66
|
+
existing.set(finding.envKey, finding.rawValue);
|
|
67
|
+
written++;
|
|
68
|
+
}
|
|
69
|
+
lines.push('');
|
|
70
|
+
}
|
|
71
|
+
if (written > 0) {
|
|
72
|
+
fs_1.default.appendFileSync(absPath, lines.join('\n'), 'utf-8');
|
|
73
|
+
}
|
|
74
|
+
return written;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=envWriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envWriter.js","sourceRoot":"","sources":["../../src/env/envWriter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitizes a string to be a valid env variable name:
|
|
3
|
+
* - Upper-case
|
|
4
|
+
* - Only alphanumeric and underscores
|
|
5
|
+
* - Cannot start with a digit
|
|
6
|
+
*/
|
|
7
|
+
export declare function sanitizeEnvKey(key: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Generates a collision-safe env key from a base name and a set of already-used keys.
|
|
10
|
+
* Appends _2, _3, ... until unique.
|
|
11
|
+
*/
|
|
12
|
+
export declare function makeUniqueKey(baseKey: string, usedKeys: Set<string>): string;
|
|
13
|
+
//# sourceMappingURL=keyNamer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyNamer.d.ts","sourceRoot":"","sources":["../../src/env/keyNamer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUlD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAO5E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeEnvKey = sanitizeEnvKey;
|
|
4
|
+
exports.makeUniqueKey = makeUniqueKey;
|
|
5
|
+
/**
|
|
6
|
+
* Sanitizes a string to be a valid env variable name:
|
|
7
|
+
* - Upper-case
|
|
8
|
+
* - Only alphanumeric and underscores
|
|
9
|
+
* - Cannot start with a digit
|
|
10
|
+
*/
|
|
11
|
+
function sanitizeEnvKey(key) {
|
|
12
|
+
let sanitized = key
|
|
13
|
+
.toUpperCase()
|
|
14
|
+
.replace(/[^A-Z0-9_]/g, '_') // replace non-alphanum/underscore with _
|
|
15
|
+
.replace(/^[0-9]+/, '_'); // cannot start with digits
|
|
16
|
+
// Collapse multiple consecutive underscores
|
|
17
|
+
sanitized = sanitized.replace(/__+/g, '_').replace(/^_|_$/g, '');
|
|
18
|
+
return sanitized || 'SECRET';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generates a collision-safe env key from a base name and a set of already-used keys.
|
|
22
|
+
* Appends _2, _3, ... until unique.
|
|
23
|
+
*/
|
|
24
|
+
function makeUniqueKey(baseKey, usedKeys) {
|
|
25
|
+
const sanitized = sanitizeEnvKey(baseKey);
|
|
26
|
+
if (!usedKeys.has(sanitized))
|
|
27
|
+
return sanitized;
|
|
28
|
+
let suffix = 2;
|
|
29
|
+
while (usedKeys.has(`${sanitized}_${suffix}`))
|
|
30
|
+
suffix++;
|
|
31
|
+
return `${sanitized}_${suffix}`;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=keyNamer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyNamer.js","sourceRoot":"","sources":["../../src/env/keyNamer.ts"],"names":[],"mappings":";;;;AAAA;;;;;GAKG;AACH,wBAA+B,GAAW;IACxC,IAAI,SAAS,GAAG,GAAG;SAChB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAG,yCAAyC;SACvE,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAO,2BAA2B;IAE7D,4CAA4C;IAC5C,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEjE,OAAO,SAAS,IAAI,QAAQ,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,uBAA8B,OAAe,EAAE,QAAqB;IAClE,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAE/C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,SAAS,IAAI,MAAM,EAAE,CAAC;QAAE,MAAM,EAAE,CAAC;IACxD,OAAO,GAAG,SAAS,IAAI,MAAM,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ScanSummary } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Shows a summary of planned changes and asks the user to confirm before proceeding.
|
|
4
|
+
* Returns true if the user approves, false if they decline.
|
|
5
|
+
*/
|
|
6
|
+
export declare function confirmChanges(summary: ScanSummary, noRewrite: boolean): Promise<boolean>;
|
|
7
|
+
/**
|
|
8
|
+
* Asks the user to confirm backup before proceeding.
|
|
9
|
+
* Returns 'backup', 'skip', or 'abort'.
|
|
10
|
+
*/
|
|
11
|
+
export declare function askBackupPreference(): Promise<'backup' | 'skip' | 'abort'>;
|
|
12
|
+
/**
|
|
13
|
+
* Prompts for a custom .env file path override.
|
|
14
|
+
*/
|
|
15
|
+
export declare function askEnvFilePath(defaultPath: string): Promise<string>;
|
|
16
|
+
//# sourceMappingURL=confirm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../src/interactive/confirm.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,WAAW,EAAE,MAAM,UAAU,CAAC;AAEhD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAmC/F;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,CAUhF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzE"}
|