fouad-env-guardian 0.1.1

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/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # `fouad-env-guardian`
2
+
3
+ CLI tool to scan projects for unsafe `.env` practices and likely secret exposure mistakes.
4
+
5
+ ## Package and command
6
+
7
+ - npm package: `fouad-env-guardian`
8
+ - CLI command: `env-guardian`
9
+
10
+ ## Install from npm
11
+
12
+ ```bash
13
+ npm install -g fouad-env-guardian
14
+ env-guardian
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```bash
20
+ env-guardian
21
+ env-guardian --version
22
+ env-guardian --help
23
+ ```
24
+
25
+ ## What it checks
26
+
27
+ - Whether `.env` exists
28
+ - Whether `.env` is tracked by git
29
+ - Whether `.env.example` exists
30
+ - Whether `.env` contains common secret-like variable names such as `API_KEY`, `SECRET`, `TOKEN`, `PASSWORD`, or `PRIVATE_KEY`
31
+
32
+ ## Example output
33
+
34
+ ```text
35
+ Env Guardian Report
36
+
37
+ Project: C:\my-project
38
+ - .env exists: yes
39
+ - .env tracked by git: yes
40
+ - .env.example exists: no
41
+ - secret-like keys found: API_KEY, TOKEN
42
+
43
+ Warnings:
44
+ - The .env file is tracked by git.
45
+ - Missing .env.example.
46
+ - Potential secret variable detected: API_KEY
47
+ - Potential secret variable detected: TOKEN
48
+ ```
49
+
50
+ ## Notes
51
+
52
+ - Run the command from the project root you want to inspect.
53
+ - The tool exits with a non-zero code when warnings are found, which makes it useful in CI.
54
+ - Detection is intentionally conservative and should be treated as a warning signal, not proof of a leak.
55
+
56
+ ## Roadmap
57
+
58
+ - Auto-fix suggestions for `.gitignore`
59
+ - Framework-aware checks for Next.js, Vite, and similar tools
60
+ - Configurable secret patterns and ignore rules
61
+ - CI examples and GitHub Actions integration
package/cli/index.mjs ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+
3
+ import chalk from "chalk";
4
+ import { createRequire } from "module";
5
+ import { scanEnv } from "../core/scan-env.mjs";
6
+
7
+ const require = createRequire(import.meta.url);
8
+ const { version } = require("../package.json");
9
+ const args = process.argv.slice(2);
10
+
11
+ if (args.includes("--help") || args.includes("-h")) {
12
+ console.log(chalk.bold("env-guardian"));
13
+ console.log("Scan the current project for unsafe .env practices.\n");
14
+ console.log(chalk.gray("Usage:"));
15
+ console.log(" env-guardian");
16
+ console.log(" env-guardian --version");
17
+ process.exit(0);
18
+ }
19
+
20
+ if (args.includes("--version") || args.includes("-v")) {
21
+ console.log(version);
22
+ process.exit(0);
23
+ }
24
+
25
+ try {
26
+ const result = scanEnv();
27
+
28
+ console.log(chalk.cyan("\nEnv Guardian Report\n"));
29
+ console.log(`Project: ${result.projectRoot}`);
30
+ console.log(`- .env exists: ${result.hasEnv ? "yes" : "no"}`);
31
+ console.log(`- .env tracked by git: ${result.envTracked ? "yes" : "no"}`);
32
+ console.log(`- .env.example exists: ${result.hasExample ? "yes" : "no"}`);
33
+
34
+ if (result.detectedSecrets.length > 0) {
35
+ console.log(`- secret-like keys found: ${result.detectedSecrets.join(", ")}`);
36
+ }
37
+
38
+ if (result.warnings.length > 0) {
39
+ console.log(chalk.yellow("\nWarnings:"));
40
+ for (const warning of result.warnings) {
41
+ console.log(chalk.yellow(`- ${warning}`));
42
+ }
43
+ process.exitCode = 1;
44
+ } else {
45
+ console.log(chalk.green("\nNo issues found"));
46
+ }
47
+ } catch (err) {
48
+ console.error(chalk.red(err?.message || String(err)));
49
+ process.exit(1);
50
+ }
@@ -0,0 +1,82 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { execFileSync } from "child_process";
4
+
5
+ const DEFAULT_SECRET_PATTERNS = [
6
+ { name: "API_KEY", regex: /^\s*[A-Z0-9_]*API[_-]?KEY\s*=/im },
7
+ { name: "SECRET", regex: /^\s*[A-Z0-9_]*SECRET\s*=/im },
8
+ { name: "TOKEN", regex: /^\s*[A-Z0-9_]*TOKEN\s*=/im },
9
+ { name: "PASSWORD", regex: /^\s*[A-Z0-9_]*PASSWORD\s*=/im },
10
+ { name: "PRIVATE_KEY", regex: /^\s*[A-Z0-9_]*PRIVATE[_-]?KEY\s*=/im },
11
+ ];
12
+
13
+ function isGitTracked(projectRoot, relativePath) {
14
+ try {
15
+ const result = execFileSync("git", ["ls-files", "--cached", "--full-name", relativePath], {
16
+ cwd: projectRoot,
17
+ stdio: ["ignore", "pipe", "pipe"],
18
+ encoding: "utf8",
19
+ });
20
+
21
+ return result.includes(relativePath);
22
+ } catch (error) {
23
+ const stderr = String(error?.stderr || "");
24
+ if (
25
+ stderr.includes("not a git repository") ||
26
+ stderr.includes("fatal: not a git repository") ||
27
+ stderr.includes("did not match any files")
28
+ ) {
29
+ return false;
30
+ }
31
+
32
+ return false;
33
+ }
34
+ }
35
+
36
+ function detectSecrets(content) {
37
+ const findings = [];
38
+
39
+ for (const pattern of DEFAULT_SECRET_PATTERNS) {
40
+ if (pattern.regex.test(content)) {
41
+ findings.push(pattern.name);
42
+ }
43
+ }
44
+
45
+ return findings;
46
+ }
47
+
48
+ export function scanEnv(projectRoot = process.cwd(), options = {}) {
49
+ const gitTrackedChecker = options.gitTrackedChecker || isGitTracked;
50
+ const envPath = path.join(projectRoot, ".env");
51
+ const examplePath = path.join(projectRoot, ".env.example");
52
+
53
+ const result = {
54
+ projectRoot,
55
+ hasEnv: fs.existsSync(envPath),
56
+ envTracked: false,
57
+ hasExample: fs.existsSync(examplePath),
58
+ warnings: [],
59
+ detectedSecrets: [],
60
+ };
61
+
62
+ if (result.hasEnv) {
63
+ result.envTracked = gitTrackedChecker(projectRoot, ".env");
64
+
65
+ if (result.envTracked) {
66
+ result.warnings.push("The .env file is tracked by git.");
67
+ }
68
+
69
+ const content = fs.readFileSync(envPath, "utf8");
70
+ result.detectedSecrets = detectSecrets(content);
71
+
72
+ for (const finding of result.detectedSecrets) {
73
+ result.warnings.push(`Potential secret variable detected: ${finding}`);
74
+ }
75
+ }
76
+
77
+ if (!result.hasExample) {
78
+ result.warnings.push("Missing .env.example.");
79
+ }
80
+
81
+ return result;
82
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "fouad-env-guardian",
3
+ "version": "0.1.1",
4
+ "description": "CLI tool to scan projects for unsafe .env practices and likely secret exposure mistakes.",
5
+ "author": "Fouad",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "keywords": [
9
+ "env",
10
+ "dotenv",
11
+ "security",
12
+ "secrets",
13
+ "cli"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/FouadBechar/NextApp.git",
18
+ "directory": "tools/env-guardian"
19
+ },
20
+ "homepage": "https://github.com/FouadBechar/NextApp/tree/main/tools/env-guardian",
21
+ "bugs": {
22
+ "url": "https://github.com/FouadBechar/NextApp/issues"
23
+ },
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "files": [
28
+ "cli",
29
+ "core",
30
+ "README.md"
31
+ ],
32
+ "bin": {
33
+ "env-guardian": "cli/index.mjs"
34
+ },
35
+ "scripts": {
36
+ "help": "node ./cli/index.mjs --help",
37
+ "test": "node ./tests/run-tests.mjs"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "dependencies": {
43
+ "chalk": "^5.6.2"
44
+ }
45
+ }