ship18ion 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.
@@ -0,0 +1,33 @@
1
+ import { RuleContext, RuleResult } from '../../engine/types';
2
+ import { findEnvUsages } from '../../engine/ast';
3
+
4
+ export async function checkNextJs(ctx: RuleContext): Promise<RuleResult[]> {
5
+ const results: RuleResult[] = [];
6
+
7
+ // 1. Check for NEXT_PUBLIC_ secrets
8
+ const codeFiles = ctx.files.filter(f => f.match(/\.(js|ts|jsx|tsx)$/));
9
+
10
+ for (const file of codeFiles) {
11
+ const usages = findEnvUsages(file);
12
+ for (const usage of usages) {
13
+ if (usage.name.startsWith('NEXT_PUBLIC_')) {
14
+ // Heuristic: Does it look like a secret?
15
+ // e.g. NEXT_PUBLIC_SECRET_KEY, NEXT_PUBLIC_API_SECRET
16
+ if (usage.name.match(/SECRET|PASSWORD|TOKEN|KEY|AUTH/i)) {
17
+ // Exception: PUBLIC_KEY is often safe
18
+ if (!usage.name.match(/PUBLIC_KEY/i)) {
19
+ results.push({
20
+ status: 'warn',
21
+ message: `Potential secret exposed via NEXT_PUBLIC_ variable: ${usage.name}`,
22
+ ruleId: 'nextjs-public-secret',
23
+ file: file,
24
+ line: usage.line
25
+ });
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+ return results;
33
+ }
@@ -0,0 +1,36 @@
1
+ import { execSync } from 'child_process';
2
+ import { RuleContext, RuleResult } from '../engine/types';
3
+
4
+ export async function checkGit(ctx: RuleContext): Promise<RuleResult[]> {
5
+ const results: RuleResult[] = [];
6
+
7
+ try {
8
+ // Check for uncommitted changes
9
+ const status = execSync('git status --porcelain', { cwd: ctx.cwd, encoding: 'utf-8' });
10
+ if (status.trim().length > 0) {
11
+ results.push({
12
+ status: 'warn',
13
+ message: 'Git working directory is dirty (uncommitted changes). Verify before shipping.',
14
+ ruleId: 'git-dirty',
15
+ });
16
+ }
17
+
18
+ // Check current branch
19
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: ctx.cwd, encoding: 'utf-8' }).trim();
20
+ const allowedBranches = ['main', 'master', 'staging', 'production', 'prod'];
21
+ if (!allowedBranches.includes(branch)) {
22
+ results.push({
23
+ status: 'warn',
24
+ message: `You are on branch '${branch}'. Production builds typically come from main/master.`,
25
+ ruleId: 'git-branch',
26
+ });
27
+ }
28
+
29
+ } catch (e) {
30
+ // Not a git repo or git not found
31
+ // Silently fail or warn?
32
+ // results.push({ status: 'warn', message: 'Not a git repository or git command failed.', ruleId: 'git-error' });
33
+ }
34
+
35
+ return results;
36
+ }
@@ -0,0 +1,53 @@
1
+ import fs from 'fs';
2
+ import { RuleContext, RuleResult } from '../engine/types';
3
+ import { SECRET_PATTERNS } from '../engine/secrets';
4
+
5
+ export async function checkSecrets(ctx: RuleContext): Promise<RuleResult[]> {
6
+ const results: RuleResult[] = [];
7
+
8
+ // Skip binary files, lock files, node_modules (already ignored by scanner but specific check here)
9
+ const filesToCheck = ctx.files.filter(f => !f.match(/\.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|lock|pdf)$/));
10
+
11
+ for (const file of filesToCheck) {
12
+ try {
13
+ const content = fs.readFileSync(file, 'utf-8');
14
+ const lines = content.split('\n');
15
+
16
+ lines.forEach((line, index) => {
17
+ // Check Regex Patterns
18
+ for (const pattern of SECRET_PATTERNS) {
19
+ if (pattern.regex.test(line)) {
20
+ results.push({
21
+ status: 'fail',
22
+ message: `Potential secret found: ${pattern.name}`,
23
+ ruleId: 'secret-pattern',
24
+ file,
25
+ line: index + 1
26
+ });
27
+ }
28
+ }
29
+
30
+ // Check Heuristics for assignments
31
+ // matches "key = '...'"
32
+ const genericMsg = line.match(/(api_?key|secret|token|password|auth)[\s]*[:=][\s]*['"]([a-zA-Z0-9_\-]{8,})['"]/i);
33
+ if (genericMsg) {
34
+ const match = genericMsg[2];
35
+ // Heuristic: Must be > 8 chars and not contain 'process.env' or template placeholders
36
+ if (match && match.length > 8 && !line.includes('process.env') && !match.includes('${')) {
37
+ results.push({
38
+ status: 'warn',
39
+ message: `Possible hardcoded secret (heuristic): ${genericMsg[1]}`,
40
+ ruleId: 'secret-heuristic',
41
+ file,
42
+ line: index + 1
43
+ });
44
+ }
45
+ }
46
+ });
47
+ } catch (e) {
48
+ // Ignore read errors
49
+ }
50
+ }
51
+
52
+ return results;
53
+ }
@@ -0,0 +1,55 @@
1
+ import fs from 'fs';
2
+ import { RuleContext, RuleResult } from '../engine/types';
3
+
4
+ export async function checkSecurity(ctx: RuleContext): Promise<RuleResult[]> {
5
+ const results: RuleResult[] = [];
6
+
7
+ const codeFiles = ctx.files.filter(f => f.match(/\.(js|ts|jsx|tsx|json)$/));
8
+
9
+ for (const file of codeFiles) {
10
+ const content = fs.readFileSync(file, 'utf-8');
11
+
12
+ // 1. Check for Hardcoded NODE_ENV not being production?
13
+ // Actually we want to verify we are NOT hardcoding 'development' in prod context?
14
+ // Or "Debug / dev configs enabled" -> debug: true
15
+
16
+ if (content.match(/debug:\s*true/)) {
17
+ results.push({
18
+ status: 'warn',
19
+ message: 'Debug mode enabled (debug: true) found',
20
+ ruleId: 'security-debug-enabled',
21
+ file
22
+ });
23
+ }
24
+
25
+ // 2. CORS Wildcard
26
+ // "origin: '*'" or "origin: *"
27
+ if (content.match(/origin:\s*['"]?\*['"]?/)) {
28
+ // Default: Enabled (fail on wildcard) unless explicitly set to false
29
+ if (ctx.config.security?.noCorsWildcard !== false) {
30
+ results.push({
31
+ status: 'fail',
32
+ message: 'CORS wildcard origin (*) detected',
33
+ ruleId: 'security-cors-wildcard',
34
+ file
35
+ });
36
+ }
37
+ }
38
+
39
+ // 3. Hardcoded credentials (simple db keywords)
40
+ // postgres://user:pass@...
41
+ if (content.match(/:\/\/[a-zA-Z0-9]+:[a-zA-Z0-9]+@/)) {
42
+ // Exclude localhost
43
+ if (!content.includes('localhost') && !content.includes('127.0.0.1')) {
44
+ results.push({
45
+ status: 'fail',
46
+ message: 'Hardcoded database credentials in connection string',
47
+ ruleId: 'security-db-creds',
48
+ file
49
+ });
50
+ }
51
+ }
52
+ }
53
+
54
+ return results;
55
+ }
@@ -0,0 +1,3 @@
1
+ DB_URL=postgres://localhost:5432/db
2
+ UNUSED_VAR=123
3
+ SECRET_KEY=supersecret
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "leaky-app",
3
+ "dependencies": {
4
+ "express": "^4.17.1",
5
+ "eslint": "8.0.0"
6
+ }
7
+ }
@@ -0,0 +1,21 @@
1
+ const express = require('express');
2
+ const app = express();
3
+
4
+ // Hardcoded secret (fake AWS key)
5
+ const awsKey = "AKIA1234567890123456";
6
+
7
+ // Missing env var usage (API_KEY is not in .env)
8
+ const apiKey = process.env.API_KEY;
9
+
10
+ // Debug mode enabled
11
+ const config = {
12
+ debug: true
13
+ };
14
+
15
+ // CORS wildcard
16
+ app.use(cors({ origin: '*' }));
17
+
18
+ // Hardcoded DB credentials
19
+ const db = "postgres://user:password@production-db.com/db";
20
+
21
+ app.listen(process.env.PORT || 3000);
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "outDir": "./dist",
6
+ "rootDir": "./src",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["node_modules", "**/*.test.ts"]
15
+ }
package/walkthrough.md ADDED
@@ -0,0 +1,51 @@
1
+ ship18ion - Production Readiness Inspector
2
+ I have successfully built ship18ion, a CLI tool to check for production readiness.
3
+
4
+ Features Implemented
5
+ 1. Environment Variable Safety
6
+ Unused Variable Detection: Scans
7
+
8
+ .env
9
+ files and code to find variables defined but never used.
10
+ Missing Variable Detection: Identifies process.env.VAR usages that lack a corresponding definition in
11
+
12
+ .env
13
+ (or config).
14
+ Format Support: Supports
15
+
16
+ .env
17
+ , .env.production files.
18
+ Robust AST Parsing: Correctly detects process.env.VAR, import.meta.env.VAR (Vite), and process.env["VAR"].
19
+ 2. Secrets Detection
20
+ Pattern Matching: Detects AWS Keys, Google API Keys, Stripe Keys, and generic private keys.
21
+ Entropy Heuristics: Detects potential high-entropy strings assigned to "secret" or "key" variables.
22
+ 3. Framework & Security Checks
23
+ Next.js Safety: Scans for NEXT_PUBLIC_ variables that appear to contain secrets (e.g. NEXT_PUBLIC_SECRET_KEY).
24
+ Git Safety: Warns if deploying from a dirty working directory or a non-production branch.
25
+ Debug Mode: Alerts on debug: true.
26
+ CORS Wildcards: Fails if origin: '*' is detected.
27
+ Database Credentials: Detects hardcoded connection strings.
28
+ 4. Dependency & Build Safety
29
+ Dev Dependencies: Warns if eslint or other dev tools are in dependencies.
30
+ Build Artifacts: Alerts if source maps (.map) or
31
+
32
+ .env
33
+ files are found in build directories.
34
+ Usage
35
+ # In your project root
36
+ npx ship18ion check
37
+ # CI Mode (minimal output)
38
+ npx ship18ion check --ci
39
+ How to Ship & Share
40
+ See
41
+
42
+ SHIPPING.md
43
+ for detailed instructions on:
44
+
45
+ Local Testing: Using npm link to test on your other projects instantly.
46
+ Publishing: Pushing to NPM so anyone can use npx ship18ion.
47
+ Architecture
48
+ CLI: Built with commander.
49
+ Engine: TypeScript-based rule engine.
50
+ Parsing: Babel-based AST parsing.
51
+ Config: ship18ion.config.json support.