vibe-safe-push 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/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # VibeSafePush 🛡️✨
2
+
3
+ **Your friendly neighborhood CLI for keeping secrets out of your code.**
4
+
5
+ SafePush is an educational tool designed for "vibe coders," beginners, and anyone who wants a simple, friendly way to learn about the dangers of accidental sensitive data exposure. It scans your local files for common secrets and credentials, teaching you *why* they're dangerous and how to handle them.
6
+
7
+ ![SafePush Demo](https://i.imgur.com/your-demo-image.gif) <!-- Placeholder for a cool demo GIF -->
8
+
9
+ ---
10
+
11
+ ## Features
12
+
13
+ ### 💖 Free Features (Always)
14
+
15
+ - **.env File Check:** Warns you if a `.env` file is present, reminding you to add it to `.gitignore`.
16
+ - **node_modules Check:** Reminds you that this hefty folder should never be in version control.
17
+ - **Basic Secret Scanning:** Scans your files for common, easy-to-spot API key patterns.
18
+ - **Educational Output:** Explains *why* each finding is a risk in simple, encouraging language.
19
+
20
+ ### 💎 Premium Features (Unlockable via Donation)
21
+
22
+ - **Advanced Secret Scanning:** Hunts for more complex and specific patterns, including:
23
+ - AWS Keys (Access Key ID & Secret)
24
+ - Firebase Configs
25
+ - JWTs (JSON Web Tokens)
26
+ - Private Key blocks (`-----BEGIN RSA PRIVATE KEY-----`)
27
+ - **Verbose Output:** Provides more detail on potential vulnerabilities.
28
+ - **Pre-commit Hook Template:** Gives you a ready-to-use Git hook to automate scans before every commit.
29
+ - **Support the Project:** Your donation helps keep this tool alive and growing!
30
+
31
+ ---
32
+
33
+ ## Installation & Usage
34
+
35
+ No installation needed! Just run it directly in your project folder using `npx`.
36
+
37
+ ```bash
38
+ npx vibe-safe-push
39
+ ```
40
+
41
+ Or, if you've cloned the repository:
42
+
43
+ ```bash
44
+ node index.js
45
+ ```
46
+
47
+ ### Example Output (Free Scan)
48
+
49
+ ```
50
+ --- Welcome to SafePush! 🛡️ ---
51
+ 💡 A friendly tool to help you avoid committing secrets to your code.
52
+ 💡 Running in free mode. For advanced scanning, unlock premium features.
53
+ 👉 Run `npx vibe-safe-push --unlock` to begin.
54
+
55
+ --- Running Free Scan ✨ ---
56
+
57
+ ⚠️ Found a .env file.
58
+ 💡 While .env files are common for local development, they should NOT be committed to version control. Make sure it is listed in your .gitignore file!
59
+
60
+ 🔥 Found a Generic API Key in: src/config.js
61
+ 💡 Found a potential API key. These keys grant access to services and should never be stored directly in code.
62
+
63
+ --- Scan Complete ---
64
+ 💡 Remember to always keep your secrets safe!
65
+ ```
66
+
67
+ ---
68
+
69
+ ## How to Unlock Premium Features 💎
70
+
71
+ We use a simple, crypto-based unlock system. No sign-ups, no credit cards, no fuss.
72
+
73
+ ### Step 1: Donate
74
+
75
+ Send a small donation (around **$2 USD** worth of SOL) to our Solana wallet. This proves you're a real human and helps us maintain the project!
76
+
77
+ **Recipient Wallet Address:**
78
+ ```
79
+ YOUR_SOLANA_WALLET_ADDRESS_HERE
80
+ ```
81
+ *(Please replace this with the actual address from `constants.js` if you are forking this project).*
82
+
83
+ ### Step 2: Get Your Transaction Details
84
+
85
+ After your transaction is confirmed on the Solana network, you'll need two pieces of information:
86
+ 1. **Your Wallet Address:** The address you sent the SOL *from*.
87
+ 2. **Transaction Signature (TXID):** The unique ID for your transaction. You can find this in your wallet's transaction history or on a Solana explorer like [Solscan](https://solscan.io/) or [Explorer by Solana Labs](https://explorer.solana.com/).
88
+
89
+ ### Step 3: Run the Unlock Command
90
+
91
+ Run the following command in your terminal:
92
+
93
+ ```bash
94
+ npx vibe-safe-push --unlock
95
+ ```
96
+
97
+ The CLI will prompt you to enter the two pieces of information from Step 2.
98
+
99
+ ```
100
+ --- Premium Unlock via Solana Donation 💎 ---
101
+ 💡 To unlock premium features, please send a small donation (e.g., ~$2 in SOL) to this address:
102
+ YOUR_SOLANA_WALLET_ADDRESS_HERE
103
+ 💡 Once sent, please provide the transaction details below.
104
+
105
+ ? What is the wallet address you sent the donation FROM? › ...
106
+ ? What is the transaction hash (signature)? › ...
107
+
108
+ 💡 Verifying transaction... This may take a moment.
109
+ ✅ Donation confirmed!
110
+ ✅ Premium features unlocked! Thank you for your support! 🙏
111
+ ```
112
+
113
+ Once unlocked, SafePush creates a `.safepush_premium` file in your project's root directory. As long as that file is there, you'll have access to premium features!
114
+
115
+ ---
116
+
117
+ ## For Vibe Coders ✌️
118
+
119
+ Coding should be fun and creative. But seeing your app on the front page of Hacker News because you accidentally leaked your AWS keys? Not a vibe.
120
+
121
+ - **Think of secrets like your house key.** You wouldn't post a picture of it on Instagram. Don't post your API keys on GitHub.
122
+ - **Use `.env` files for your secrets.** These files let you use your keys in your local project without ever writing them in your main code.
123
+ - **Always add `.env` to your `.gitignore` file.** This is the magic spell that tells Git, "Hey, ignore this file, it's just for me."
124
+
125
+ Happy coding, and stay safe!
package/constants.js ADDED
@@ -0,0 +1,12 @@
1
+ // ⚠️ IMPORTANT: Replace with your actual Solana wallet address.
2
+ // This is the address where users will send their donation.
3
+ const RECIPIENT_WALLET_ADDRESS = 'GgANeKwJecCMPhna9HvZCtELUCg3c6snJZsqi8vx2JqW';
4
+
5
+ // A free, public Solana RPC endpoint.
6
+ // You can find more at https://solana.com/rpc
7
+ const SOLANA_RPC_ENDPOINT = 'https://api.mainnet-beta.solana.com';
8
+
9
+ module.exports = {
10
+ RECIPIENT_WALLET_ADDRESS,
11
+ SOLANA_RPC_ENDPOINT,
12
+ };
package/index.js ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const axios = require('axios');
5
+ const prompts = require('prompts');
6
+ const minimist = require('minimist');
7
+ const chalk = require('chalk');
8
+ const logger = require('./utils/logger');
9
+ const basicScanner = require('./scanners/basicScanner');
10
+ const advancedScanner = require('./scanners/advancedScanner');
11
+ const { RECIPIENT_WALLET_ADDRESS, SOLANA_RPC_ENDPOINT } = require('./constants');
12
+
13
+ const PREMIUM_LOCK_FILE = '.safepush_premium';
14
+
15
+ function isPremiumUnlocked() {
16
+ return fs.existsSync(PREMIUM_LOCK_FILE);
17
+ }
18
+
19
+ function unlockPremium() {
20
+ fs.writeFileSync(PREMIUM_LOCK_FILE, 'unlocked');
21
+ logger.success('Premium features unlocked! Thank you for your support! 🙏');
22
+ }
23
+
24
+ async function verifyDonation() {
25
+ logger.header('Premium Unlock via Solana Donation 💎');
26
+ logger.info(`To unlock premium features, please send a small donation (e.g., ~$2 in SOL) to this address:`);
27
+ logger.log(chalk.bold.cyan(RECIPIENT_WALLET_ADDRESS));
28
+ logger.info('Once sent, please provide the transaction details below.');
29
+
30
+ const response = await prompts([
31
+ {
32
+ type: 'text',
33
+ name: 'fromAddress',
34
+ message: 'What is the wallet address you sent the donation FROM?',
35
+ },
36
+ {
37
+ type: 'text',
38
+ name: 'txid',
39
+ message: 'What is the transaction hash (signature)?',
40
+ },
41
+ ]);
42
+
43
+ if (!response.fromAddress || !response.txid) {
44
+ logger.danger('Unlock process cancelled.');
45
+ return;
46
+ }
47
+
48
+ logger.info('Verifying transaction... This may take a moment.');
49
+
50
+ try {
51
+ const rpcResponse = await axios.post(SOLANA_RPC_ENDPOINT, {
52
+ jsonrpc: '2.0',
53
+ id: 1,
54
+ method: 'getTransaction',
55
+ params: [response.txid, 'json'],
56
+ });
57
+
58
+ const transaction = rpcResponse.data.result;
59
+
60
+ if (!transaction) {
61
+ throw new Error('Transaction not found. Make sure the transaction has been confirmed on the network.');
62
+ }
63
+
64
+ const { postBalances, preBalances } = transaction.meta;
65
+ const accountKeys = transaction.transaction.message.accountKeys;
66
+ const fromIndex = accountKeys.findIndex(acc => acc === response.fromAddress);
67
+ const toIndex = accountKeys.findIndex(acc => acc === RECIPIENT_WALLET_ADDRESS);
68
+
69
+ if (fromIndex === -1 || toIndex === -1) {
70
+ throw new Error('The "from" or "to" address does not match the transaction details.');
71
+ }
72
+
73
+ const fromBalanceChange = postBalances[fromIndex] - preBalances[fromIndex];
74
+ const toBalanceChange = postBalances[toIndex] - preBalances[toIndex];
75
+
76
+ if (fromBalanceChange < 0 && toBalanceChange > 0) {
77
+ logger.success('Donation confirmed!');
78
+ unlockPremium();
79
+ } else {
80
+ throw new Error('Transaction details are incorrect. Could not verify the transfer.');
81
+ }
82
+
83
+ } catch (error) {
84
+ logger.danger('Verification failed!');
85
+ logger.info(error.message || 'Please double-check your transaction hash and wallet address. It can also take a few minutes for a transaction to be confirmed.');
86
+ }
87
+ }
88
+
89
+
90
+ async function main() {
91
+ const args = minimist(process.argv.slice(2));
92
+
93
+ logger.log(chalk.bold.magenta('--- Welcome to SafePush! 🛡️ ---'));
94
+ logger.info('A friendly tool to help you avoid committing secrets to your code.');
95
+
96
+ if (args.unlock) {
97
+ await verifyDonation();
98
+ return;
99
+ }
100
+
101
+ const premium = isPremiumUnlocked();
102
+
103
+ if (premium) {
104
+ logger.success('Premium features are enabled. Running full scan.');
105
+ } else {
106
+ logger.info('Running in free mode. For advanced scanning, unlock premium features.');
107
+ logger.link('Run `npx safe-push --unlock` to begin.');
108
+ }
109
+
110
+ await basicScanner();
111
+
112
+ if (premium) {
113
+ await advancedScanner();
114
+ }
115
+
116
+ logger.header('Scan Complete');
117
+ logger.info('Remember to always keep your secrets safe!');
118
+ }
119
+
120
+ main();
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "vibe-safe-push",
3
+ "version": "1.0.0",
4
+ "description": "A beginner-friendly CLI to learn about accidental sensitive data exposure.",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "safe-push": "index.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node index.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/a-zmuth/vibe-safe-push.git"
15
+ },
16
+ "keywords": [
17
+ "security",
18
+ "education",
19
+ "cli",
20
+ "secrets",
21
+ "solana"
22
+ ],
23
+ "author": "Azmoth (Chris Alih)",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "axios": "^1.6.8",
27
+ "chalk": "^4.1.2",
28
+ "glob": "^10.3.10",
29
+ "minimist": "^1.2.8",
30
+ "prompts": "^2.4.2"
31
+ }
32
+ }
@@ -0,0 +1,37 @@
1
+ // Advanced patterns for the premium scan.
2
+ // These are more specific and cover a wider range of sensitive data.
3
+
4
+ const premiumPatterns = [
5
+ {
6
+ name: 'AWS Access Key ID',
7
+ regex: /AKIA[0-9A-Z]{16}/,
8
+ explanation: 'Found an AWS Access Key ID. When paired with a Secret Access Key, this grants full programmatic access to your AWS account.',
9
+ },
10
+ {
11
+ name: 'AWS Secret Access Key',
12
+ regex: /[0-9a-zA-Z\/+=]{40}/,
13
+ explanation: 'Found a string that looks like an AWS Secret Access Key. This is highly sensitive and provides administrator-level access to your AWS resources.',
14
+ },
15
+ {
16
+ name: 'Firebase Config',
17
+ regex: /"firebase":\s*\{/,
18
+ explanation: 'Found a Firebase configuration block. This can expose your database URL, API keys, and other sensitive project details.',
19
+ },
20
+ {
21
+ name: 'JSON Web Token (JWT)',
22
+ regex: /ey[a-zA-Z0-9_-]+\.ey[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/,
23
+ explanation: 'Found a JSON Web Token (JWT). If this token is not expired, it can be used to impersonate a user and access protected routes in an application.',
24
+ },
25
+ {
26
+ name: 'Private Key',
27
+ regex: /-----BEGIN ((RSA|OPENSSH|EC|PGP) )?PRIVATE KEY-----/,
28
+ explanation: 'Found a private key block. Private keys are the ultimate secret and are used for encryption and signing. They should never be hardcoded.',
29
+ },
30
+ {
31
+ name: 'OAuth 2.0 Client Secret',
32
+ regex: /client_secret\s*=\s*['"]?[a-zA-Z0-9_.-]+['"]?/,
33
+ explanation: 'Found an OAuth 2.0 Client Secret. This secret is used to authenticate your application with an OAuth provider.',
34
+ },
35
+ ];
36
+
37
+ module.exports = premiumPatterns;
package/patterns.js ADDED
@@ -0,0 +1,22 @@
1
+ // Basic patterns for the free scan.
2
+ // These are simple, common patterns that are easy for beginners to understand.
3
+
4
+ const basicPatterns = [
5
+ {
6
+ name: 'Generic API Key',
7
+ regex: /api[_-]?key\s*=\s*['"]?[a-zA-Z0-9_.-]+['"]?/i,
8
+ explanation: 'Found a potential API key. These keys grant access to services and should never be stored directly in code.',
9
+ },
10
+ {
11
+ name: 'Google API Key',
12
+ regex: /AIza[0-9A-Za-z\-_]{35}/,
13
+ explanation: 'Found a Google API Key. Exposing this can lead to misuse of your Google Cloud services and unexpected charges.',
14
+ },
15
+ {
16
+ name: 'Stripe API Key',
17
+ regex: /(?:r|s)k_live_[0-9a-zA-Z]{24}/,
18
+ explanation: 'Found a Stripe API Key. This key can be used to make payments and access your Stripe account data. Keep it secret!',
19
+ },
20
+ ];
21
+
22
+ module.exports = basicPatterns;
@@ -0,0 +1,56 @@
1
+ const fs = require('fs');
2
+ const { glob } = require('glob');
3
+ const logger = require('../utils/logger');
4
+ const premiumPatterns = require('../patterns-premium');
5
+
6
+ const IGNORE_PATTERNS = ['node_modules/**', 'package-lock.json', 'yarn.lock'];
7
+
8
+ async function advancedScanner() {
9
+ logger.header('Running Premium Scan 💎');
10
+ let findings = 0;
11
+
12
+ const files = await glob('**/*', { nodir: true, ignore: IGNORE_PATTERNS });
13
+
14
+ for (const file of files) {
15
+ const content = fs.readFileSync(file, 'utf8');
16
+ for (const pattern of premiumPatterns) {
17
+ if (pattern.regex.test(content)) {
18
+ logger.danger(`Found a ${pattern.name} in: ${file}`);
19
+ logger.info(pattern.explanation);
20
+ findings++;
21
+ }
22
+ }
23
+ }
24
+
25
+ // Offer pre-commit hook template
26
+ logger.info('As a premium user, you can use a pre-commit hook to automate these checks.');
27
+ logger.log('To set it up, create a file at .git/hooks/pre-commit and add this content:');
28
+ const hookScript = `
29
+ #!/bin/sh
30
+ #
31
+ # A basic pre-commit hook to run safe-push before committing.
32
+ #
33
+
34
+ echo "Running safe-push scan before commit..."
35
+ npx safe-push
36
+
37
+ # If the scan fails (exits with a non-zero code), prevent the commit
38
+ if [ $? -ne 0 ]; then
39
+ echo "Commit aborted by safe-push scan. Please fix the issues."
40
+ exit 1
41
+ fi
42
+
43
+ echo "Scan passed. Proceeding with commit."
44
+ exit 0
45
+ `;
46
+ logger.log(chalk.gray(hookScript));
47
+
48
+
49
+ if (findings === 0) {
50
+ logger.success('No advanced vulnerabilities found. Your code looks very secure! 🚀');
51
+ } else {
52
+ logger.warning(`Found ${findings} potential issues in the premium scan.`);
53
+ }
54
+ }
55
+
56
+ module.exports = advancedScanner;
@@ -0,0 +1,48 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { glob } = require('glob');
4
+ const logger = require('../utils/logger');
5
+ const basicPatterns = require('../patterns');
6
+
7
+ const IGNORE_PATTERNS = ['node_modules/**', 'package-lock.json', 'yarn.lock'];
8
+
9
+ async function basicScanner() {
10
+ logger.header('Running Free Scan ✨');
11
+ let findings = 0;
12
+
13
+ // 1. Check for .env file
14
+ if (fs.existsSync('.env')) {
15
+ logger.warning('Found a .env file.');
16
+ logger.info('While .env files are common for local development, they should NOT be committed to version control. Make sure it is listed in your .gitignore file!');
17
+ findings++;
18
+ }
19
+
20
+ // 2. Check for node_modules directory
21
+ if (fs.existsSync('node_modules')) {
22
+ logger.warning('Found a node_modules directory.');
23
+ logger.info('This directory can contain thousands of files and should never be committed to version control. Ensure it is in your .gitignore file.');
24
+ findings++;
25
+ }
26
+
27
+ // 3. Scan files for basic patterns
28
+ const files = await glob('**/*', { nodir: true, ignore: IGNORE_PATTERNS });
29
+
30
+ for (const file of files) {
31
+ const content = fs.readFileSync(file, 'utf8');
32
+ for (const pattern of basicPatterns) {
33
+ if (pattern.regex.test(content)) {
34
+ logger.danger(`Found a ${pattern.name} in: ${file}`);
35
+ logger.info(pattern.explanation);
36
+ findings++;
37
+ }
38
+ }
39
+ }
40
+
41
+ if (findings === 0) {
42
+ logger.success('No basic vulnerabilities found. Good job! 🎉');
43
+ } else {
44
+ logger.warning(`Found ${findings} potential issues in the free scan.`);
45
+ }
46
+ }
47
+
48
+ module.exports = basicScanner;
@@ -0,0 +1,23 @@
1
+
2
+ const chalk = require('chalk');
3
+
4
+ const log = (message) => console.log(message);
5
+
6
+ const info = (message) => log(chalk.blue(`💡 ${message}`));
7
+ const success = (message) => log(chalk.green(`✅ ${message}`));
8
+ const warning = (message) => log(chalk.yellow(`⚠️ ${message}`));
9
+ const danger = (message) => log(chalk.red(`🔥 ${message}`));
10
+ const header = (message) => log(chalk.bold.magenta(`
11
+ --- ${message} ---
12
+ `));
13
+ const link = (message) => log(chalk.cyan.underline(message));
14
+
15
+ module.exports = {
16
+ log,
17
+ info,
18
+ success,
19
+ warning,
20
+ danger,
21
+ header,
22
+ link,
23
+ };