dotsnap 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/.env ADDED
@@ -0,0 +1,11 @@
1
+ # Production Environment
2
+ TIMOTHY_KEY=sk-live-1234567890abcdef
3
+ ABIGAIL_PASSWORD=myPassword123
4
+ STRIPE_SECRET_KEY=sk_test_51ABC123xyz789
5
+ DATABASE_URL=postgresql://user:pass@localhost:5432/db
6
+ JWT_SECRET=super-secret-jwt-token-12345
7
+
8
+ # AWS Configuration
9
+ AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
10
+ AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
11
+ NEXT_PUBLIC_APP_URL=https://myapp.com
package/.env.safe ADDED
@@ -0,0 +1,11 @@
1
+ # Production Environment
2
+ TIMOTHY_KEY=sk-*****************cdef
3
+ ABIGAIL_PASSWORD=myP******d123
4
+ STRIPE_SECRET_KEY=sk_***************z789
5
+ DATABASE_URL=pos*********************************2/db
6
+ JWT_SECRET=sup*********************2345
7
+
8
+ # AWS Configuration
9
+ AWS_ACCESS_KEY_ID=AKI*************MPLE
10
+ AWS_SECRET_ACCESS_KEY=wJa*********************************EKEY
11
+ NEXT_PUBLIC_APP_URL=htt**********.com
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # DotSnap CLI
2
+
3
+ Mask your .env files securely - from the command line.
4
+
5
+ ## Installation
6
+
7
+ ### Global Installation
8
+
9
+ ```bash
10
+ npm install -g dotsnap
11
+ ```
12
+
13
+ ### NPX (No Installation)
14
+
15
+ ```bash
16
+ npx dotsnap mask
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Mask your .env file
22
+
23
+ ```bash
24
+ dotsnap mask
25
+ ```
26
+
27
+ This will:
28
+
29
+ - Read your `.env` file
30
+ - Mask all secret values
31
+ - Save to `.env.safe`
32
+
33
+ ### Custom input/output
34
+
35
+ ```bash
36
+ dotsnap mask -i .env.production -o .env.production.safe
37
+ ```
38
+
39
+ ### Fully hide values
40
+
41
+ ```bash
42
+ dotsnap mask --fully-hide
43
+ ```
44
+
45
+ ### Open Web UI
46
+
47
+ ```bash
48
+ dotsnap web
49
+ ```
50
+
51
+ ## Options
52
+
53
+ ### `dotsnap mask`
54
+
55
+ | Option | Description | Default |
56
+ | --------------------- | --------------------------- | ---------------- |
57
+ | `-i, --input <file>` | Input .env file | `.env` |
58
+ | `-o, --output <file>` | Output file | `.env.safe` |
59
+ | `--fully-hide` | Hide all values completely | `false` |
60
+ | `--no-first-last` | Don't show first/last chars | Shows by default |
61
+
62
+ ### `dotsnap web`
63
+
64
+ | Option | Description | Default |
65
+ | ------------------- | -------------- | ------- |
66
+ | `-p, --port <port>` | Port to run on | `3000` |
67
+
68
+ ## Examples
69
+
70
+ ```bash
71
+ # Basic usage
72
+ dotsnap mask
73
+
74
+ # Custom files
75
+ dotsnap mask -i .env.prod -o .env.prod.safe
76
+
77
+ # Fully hide all values
78
+ dotsnap mask --fully-hide
79
+
80
+ # Open web UI
81
+ dotsnap web
82
+ ```
83
+
84
+ ## Security
85
+
86
+ - 100% local processing
87
+ - No network requests
88
+ - No data collection
89
+ - Open source
90
+
91
+ ## License
92
+
93
+ MIT
94
+
95
+ ```
96
+
97
+ # cli/.npmignore
98
+ ```
99
+
100
+ node_modules/
101
+ \*.log
102
+ .DS_Store
103
+ test/
104
+ .git/
105
+ .github/
package/bin/dotsnap.js ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require("commander");
4
+ const chalk = require("chalk");
5
+ const packageJson = require("../package.json");
6
+ const { maskCommand } = require("../src/commands/mask");
7
+ const { webCommand } = require("../src/commands/web");
8
+
9
+ program
10
+ .name("dotsnap")
11
+ .description("Mask your .env files securely")
12
+ .version(packageJson.version);
13
+
14
+ program
15
+ .command("mask")
16
+ .description("Mask secrets in your .env file")
17
+ .option("-i, --input <file>", "Input .env file", ".env")
18
+ .option("-o, --output <file>", "Output file", ".env.safe")
19
+ .option("--fully-hide", "Completely hide all values with ****")
20
+ .option("--no-first-last", "Don't show first/last characters")
21
+ .action(maskCommand);
22
+
23
+ program
24
+ .command("web")
25
+ .description("Open the web UI in your browser")
26
+ .option("-p, --port <port>", "Port to run on", "3000")
27
+ .action(webCommand);
28
+
29
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "dotsnap",
3
+ "version": "1.0.0",
4
+ "description": "Mask your .env files securely - CLI and Web UI",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "dotsnap": "bin/dotsnap.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"No tests yet\" && exit 0"
11
+ },
12
+ "keywords": [
13
+ "env",
14
+ "environment",
15
+ "secrets",
16
+ "security",
17
+ "mask",
18
+ "dotenv",
19
+ "cli"
20
+ ],
21
+ "author": "Your Name <Timothy Okoduwa>",
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/timothy-okoduwa/dotsnap.git"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/timothy-okoduwa/dotsnap/issues"
29
+ },
30
+ "homepage": "https://github.com/timothy-okoduwa/dotsnap#readme",
31
+ "dependencies": {
32
+ "chalk": "^4.1.2",
33
+ "commander": "^11.1.0",
34
+ "ora": "^5.4.1"
35
+ },
36
+ "engines": {
37
+ "node": ">=14.0.0"
38
+ }
39
+ }
@@ -0,0 +1,60 @@
1
+ const chalk = require("chalk");
2
+ const ora = require("ora");
3
+ const {
4
+ readEnvFile,
5
+ writeEnvFile,
6
+ fileExists,
7
+ } = require("../utils/fileSystem");
8
+ const { processEnvContent } = require("../utils/mask");
9
+ const { logSuccess, logError, logInfo } = require("../utils/logger");
10
+
11
+ async function maskCommand(options) {
12
+ const spinner = ora("Processing .env file...").start();
13
+
14
+ try {
15
+ // Check if input file exists
16
+ if (!fileExists(options.input)) {
17
+ spinner.fail();
18
+ logError(`File not found: ${options.input}`);
19
+ process.exit(1);
20
+ }
21
+
22
+ // Read the .env file
23
+ spinner.text = "Reading .env file...";
24
+ const content = readEnvFile(options.input);
25
+
26
+ // Count secrets
27
+ const secretCount = content
28
+ .split("\n")
29
+ .filter(
30
+ (line) => !line.trim().startsWith("#") && line.includes("="),
31
+ ).length;
32
+
33
+ // Mask the content
34
+ spinner.text = "Masking secrets...";
35
+ const showFirstLast = options.firstLast !== false;
36
+ const fullyHide = options.fullyHide || false;
37
+ const maskedContent = processEnvContent(content, showFirstLast, fullyHide);
38
+
39
+ // Write to output file
40
+ spinner.text = "Saving masked file...";
41
+ writeEnvFile(options.output, maskedContent);
42
+
43
+ spinner.succeed();
44
+
45
+ // Success messages
46
+ console.log("");
47
+ logSuccess(`Found .env file`);
48
+ logSuccess(`Masked ${secretCount} secrets`);
49
+ logSuccess(`Saved to ${options.output}`);
50
+ console.log("");
51
+ logInfo(`You can now safely share ${chalk.cyan(options.output)}`);
52
+ console.log("");
53
+ } catch (error) {
54
+ spinner.fail();
55
+ logError(`Failed to mask secrets: ${error.message}`);
56
+ process.exit(1);
57
+ }
58
+ }
59
+
60
+ module.exports = { maskCommand };
@@ -0,0 +1,30 @@
1
+ const chalk = require("chalk");
2
+ const { logInfo, logSuccess, logError } = require("../utils/logger");
3
+
4
+ async function webCommand(options) {
5
+ const port = options.port || 3000;
6
+
7
+ console.log("");
8
+ logInfo("Opening DotSnap Web UI...");
9
+ console.log("");
10
+
11
+ // Try to open the hosted web version
12
+ const url = "https://dotsnap.com/tool"; // Replace with your actual domain
13
+
14
+ logSuccess(`Opening browser at: ${chalk.cyan(url)}`);
15
+ console.log("");
16
+ logInfo("If the browser doesn't open automatically, visit:");
17
+ console.log(` ${chalk.cyan(url)}`);
18
+ console.log("");
19
+
20
+ // Try to open browser
21
+ const open = require("open");
22
+ try {
23
+ await open(url);
24
+ } catch (error) {
25
+ logError("Could not open browser automatically");
26
+ logInfo(`Please visit: ${chalk.cyan(url)}`);
27
+ }
28
+ }
29
+
30
+ module.exports = { webCommand };
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ const { processEnvContent, maskSecret } = require("./utils/mask");
2
+
3
+ module.exports = {
4
+ processEnvContent,
5
+ maskSecret,
6
+ };
@@ -0,0 +1,28 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ function fileExists(filePath) {
5
+ return fs.existsSync(filePath);
6
+ }
7
+
8
+ function readEnvFile(filePath) {
9
+ try {
10
+ return fs.readFileSync(filePath, "utf8");
11
+ } catch (error) {
12
+ throw new Error(`Failed to read file: ${error.message}`);
13
+ }
14
+ }
15
+
16
+ function writeEnvFile(filePath, content) {
17
+ try {
18
+ fs.writeFileSync(filePath, content, "utf8");
19
+ } catch (error) {
20
+ throw new Error(`Failed to write file: ${error.message}`);
21
+ }
22
+ }
23
+
24
+ module.exports = {
25
+ fileExists,
26
+ readEnvFile,
27
+ writeEnvFile,
28
+ };
@@ -0,0 +1,24 @@
1
+ const chalk = require("chalk");
2
+
3
+ function logSuccess(message) {
4
+ console.log(chalk.green("✓"), message);
5
+ }
6
+
7
+ function logError(message) {
8
+ console.log(chalk.red("✗"), message);
9
+ }
10
+
11
+ function logInfo(message) {
12
+ console.log(chalk.blue("ℹ"), message);
13
+ }
14
+
15
+ function logWarning(message) {
16
+ console.log(chalk.yellow("⚠"), message);
17
+ }
18
+
19
+ module.exports = {
20
+ logSuccess,
21
+ logError,
22
+ logInfo,
23
+ logWarning,
24
+ };
@@ -0,0 +1,56 @@
1
+ function maskSecret(value, showFirstLast = true, fullyHide = false) {
2
+ if (!value) return value;
3
+
4
+ const unquoted = value.replace(/^["']|["']$/g, "");
5
+
6
+ if (fullyHide) {
7
+ return "****";
8
+ }
9
+
10
+ const len = unquoted.length;
11
+ if (len <= 4) {
12
+ return "****";
13
+ }
14
+
15
+ if (!showFirstLast) {
16
+ return "*".repeat(len);
17
+ }
18
+
19
+ const firstChars = unquoted.slice(0, 3);
20
+ const lastChars = unquoted.slice(-4);
21
+ const maskedMiddle = "*".repeat(Math.max(len - 7, 4));
22
+
23
+ return firstChars + maskedMiddle + lastChars;
24
+ }
25
+
26
+ function processEnvContent(content, showFirstLast = true, fullyHide = false) {
27
+ const lines = content.split("\n");
28
+
29
+ return lines
30
+ .map((line) => {
31
+ if (!line.trim() || line.trim().startsWith("#")) {
32
+ return line;
33
+ }
34
+
35
+ const equalsIndex = line.indexOf("=");
36
+ if (equalsIndex === -1) {
37
+ return line;
38
+ }
39
+
40
+ const key = line.slice(0, equalsIndex).trim();
41
+ const value = line.slice(equalsIndex + 1).trim();
42
+
43
+ if (!value) {
44
+ return line;
45
+ }
46
+
47
+ const maskedValue = maskSecret(value, showFirstLast, fullyHide);
48
+ return `${key}=${maskedValue}`;
49
+ })
50
+ .join("\n");
51
+ }
52
+
53
+ module.exports = {
54
+ maskSecret,
55
+ processEnvContent,
56
+ };