ship18ion 1.1.3 → 1.2.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/CONTRIBUTING.md +60 -0
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/engine/ast.d.ts +6 -0
- package/dist/engine/config.d.ts +12 -0
- package/dist/engine/detector.d.ts +2 -0
- package/dist/engine/runner.d.ts +3 -0
- package/dist/engine/runner.js +9 -0
- package/dist/engine/scanner.d.ts +1 -0
- package/dist/engine/secrets.d.ts +6 -0
- package/{src/engine/types.ts → dist/engine/types.d.ts} +15 -17
- package/dist/reporters/console.d.ts +2 -0
- package/dist/reporters/console.js +3 -0
- package/dist/rules/build.d.ts +3 -0
- package/dist/rules/env.d.ts +2 -0
- package/dist/rules/frameworks/nextjs.d.ts +2 -0
- package/dist/rules/git.d.ts +2 -0
- package/dist/rules/hygiene.d.ts +2 -0
- package/dist/rules/hygiene.js +47 -0
- package/dist/rules/packages.d.ts +2 -0
- package/dist/rules/packages.js +32 -0
- package/dist/rules/secrets.d.ts +2 -0
- package/dist/rules/security.d.ts +2 -0
- package/package.json +9 -3
- package/SHIPPING.md +0 -57
- package/src/cli/index.ts +0 -56
- package/src/engine/ast.ts +0 -84
- package/src/engine/config.ts +0 -28
- package/src/engine/detector.ts +0 -27
- package/src/engine/runner.ts +0 -53
- package/src/engine/scanner.ts +0 -22
- package/src/engine/secrets.ts +0 -26
- package/src/reporters/console.ts +0 -66
- package/src/rules/build.ts +0 -77
- package/src/rules/env.ts +0 -99
- package/src/rules/frameworks/nextjs.ts +0 -33
- package/src/rules/git.ts +0 -95
- package/src/rules/secrets.ts +0 -53
- package/src/rules/security.ts +0 -55
- package/tests/fixtures/leaky-app/.env +0 -3
- package/tests/fixtures/leaky-app/package.json +0 -7
- package/tests/fixtures/leaky-app/src/index.js +0 -21
- package/tsconfig.json +0 -15
- package/walkthrough.md +0 -51
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Contributing to ship18ion
|
|
2
|
+
|
|
3
|
+
First off, thanks for taking the time to contribute! ❤️
|
|
4
|
+
|
|
5
|
+
All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [I Have a Question](#i-have-a-question)
|
|
10
|
+
- [I Want To Contribute](#i-want-to-contribute)
|
|
11
|
+
- [Reporting Bugs](#reporting-bugs)
|
|
12
|
+
- [Suggesting Enhancements](#suggesting-enhancements)
|
|
13
|
+
- [Your First Code Contribution](#your-first-code-contribution)
|
|
14
|
+
|
|
15
|
+
## I Have a Question
|
|
16
|
+
|
|
17
|
+
> If you want to ask a question, we assume that you have read the available [Documentation](README.md).
|
|
18
|
+
|
|
19
|
+
Before you ask a question, it is best to search for existing [Issues](/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
|
|
20
|
+
|
|
21
|
+
## I Want To Contribute
|
|
22
|
+
|
|
23
|
+
### Reporting Bugs
|
|
24
|
+
|
|
25
|
+
- Make sure that you are using the latest version.
|
|
26
|
+
- Read the documentation carefully and find out if the functionality is already covered, maybe by an individual configuration.
|
|
27
|
+
- Perform a search to see if the problem has already been reported. If it has, add a comment to the existing issue instead of opening a new one.
|
|
28
|
+
|
|
29
|
+
### Suggesting Enhancements
|
|
30
|
+
|
|
31
|
+
- Open a new issue and use the **Feature Request** template.
|
|
32
|
+
- Explain why this enhancement would be useful to most users.
|
|
33
|
+
|
|
34
|
+
## Styleguides
|
|
35
|
+
|
|
36
|
+
### Commit Messages
|
|
37
|
+
|
|
38
|
+
- Use [Conventional Commits](https://www.conventionalcommits.org/).
|
|
39
|
+
|
|
40
|
+
### Code Style
|
|
41
|
+
|
|
42
|
+
- Keep code clean and use the existing ESLint/Prettier configs (if available).
|
|
43
|
+
- Add tests for new features.
|
|
44
|
+
|
|
45
|
+
## Development Workflow
|
|
46
|
+
|
|
47
|
+
To test your changes locally on another project without publishing:
|
|
48
|
+
|
|
49
|
+
1. **Build and Link**:
|
|
50
|
+
```bash
|
|
51
|
+
npm run build
|
|
52
|
+
npm link
|
|
53
|
+
```
|
|
54
|
+
2. **Use in Target Project**:
|
|
55
|
+
```bash
|
|
56
|
+
npm link ship18ion
|
|
57
|
+
npx ship18ion
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Thank you!
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 champ18ion
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -31,6 +31,17 @@ Think of it as `eslint` but for **deployability**.
|
|
|
31
31
|
- Ensures `.env` files are not bundled into build output directories.
|
|
32
32
|
- Checks for dev dependencies (like `eslint`) accidentally listed in `dependencies`.
|
|
33
33
|
|
|
34
|
+
- **🧹 Code Hygiene (New)**:
|
|
35
|
+
- Warns on leftover `console.log()` calls.
|
|
36
|
+
- Flags `FIXME` comments that need resolution.
|
|
37
|
+
|
|
38
|
+
- **📦 Dependencies (New)**:
|
|
39
|
+
- Checks for duplicate packages (listed in both `dependencies` and `devDependencies`).
|
|
40
|
+
|
|
41
|
+
- **🐙 Git Safety (New)**:
|
|
42
|
+
- Ensures critical files (`node_modules`, `.env`) and framework artifacts (`.next`) are git-ignored.
|
|
43
|
+
- **Security**: alerts if dangerous keys (e.g. `serviceAccountKey.json`) exist but are *not* ignored.
|
|
44
|
+
|
|
34
45
|
- **👷 CI/CD Ready**:
|
|
35
46
|
- Zero config by default.
|
|
36
47
|
- Returns exit code `1` on failure to block bad builds.
|
|
@@ -95,6 +106,11 @@ npx ship18ion check --ci
|
|
|
95
106
|
| **Next.js** | `nextjs-public-secret` | High-entropy string found in `NEXT_PUBLIC_` variable. |
|
|
96
107
|
| **Security** | `security-cors` | Detects wildcard `Access-Control-Allow-Origin`. |
|
|
97
108
|
| **Git** | `git-dirty` | Warns if deploying with uncommitted changes. |
|
|
109
|
+
| **Git** | `git-ignore-missing` | Warns if `.gitignore` is missing critical entries (`node_modules`, `.env`). |
|
|
110
|
+
| **Git** | `git-ignore-auth` | **Critical**: Fails if `serviceAccountKey.json` etc are not ignored. |
|
|
111
|
+
| **Hygiene** | `hygiene-console-log` | Warns on `console.log` in production code. |
|
|
112
|
+
| **Hygiene** | `hygiene-fixme` | Warns on leftover `FIXME` comments. |
|
|
113
|
+
| **Package** | `package-duplicate` | Warns if a package is in both dependency lists. |
|
|
98
114
|
|
|
99
115
|
## 🤝 Contributing
|
|
100
116
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface Ship18ionConfig {
|
|
2
|
+
env?: {
|
|
3
|
+
required?: string[];
|
|
4
|
+
disallowed?: string[];
|
|
5
|
+
};
|
|
6
|
+
security?: {
|
|
7
|
+
noCorsWildcard?: boolean;
|
|
8
|
+
requireRateLimit?: boolean;
|
|
9
|
+
};
|
|
10
|
+
ignore?: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function loadConfig(cwd?: string): Promise<Ship18ionConfig>;
|
package/dist/engine/runner.js
CHANGED
|
@@ -6,6 +6,8 @@ const env_1 = require("../rules/env");
|
|
|
6
6
|
const secrets_1 = require("../rules/secrets");
|
|
7
7
|
const security_1 = require("../rules/security");
|
|
8
8
|
const build_1 = require("../rules/build");
|
|
9
|
+
const hygiene_1 = require("../rules/hygiene");
|
|
10
|
+
const packages_1 = require("../rules/packages");
|
|
9
11
|
const nextjs_1 = require("../rules/frameworks/nextjs");
|
|
10
12
|
const git_1 = require("../rules/git");
|
|
11
13
|
const detector_1 = require("./detector");
|
|
@@ -33,6 +35,13 @@ async function runChecks(config, cwd, onProgress) {
|
|
|
33
35
|
if (onProgress)
|
|
34
36
|
onProgress('Inspecting build artifacts...');
|
|
35
37
|
results.push(...await (0, build_1.checkBuild)(ctx));
|
|
38
|
+
// New Rules
|
|
39
|
+
if (onProgress)
|
|
40
|
+
onProgress('Checking code hygiene...');
|
|
41
|
+
results.push(...await (0, hygiene_1.checkHygiene)(ctx));
|
|
42
|
+
if (onProgress)
|
|
43
|
+
onProgress('Validating packages...');
|
|
44
|
+
results.push(...await (0, packages_1.checkPackages)(ctx));
|
|
36
45
|
// Framework specific checks
|
|
37
46
|
if (framework === 'nextjs') {
|
|
38
47
|
if (onProgress)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function scanFiles(cwd: string, ignore?: string[]): Promise<string[]>;
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { Ship18ionConfig } from './config';
|
|
2
|
-
import { FrameworkType } from './detector';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
framework: FrameworkType;
|
|
17
|
-
}
|
|
1
|
+
import { Ship18ionConfig } from './config';
|
|
2
|
+
import { FrameworkType } from './detector';
|
|
3
|
+
export interface RuleResult {
|
|
4
|
+
status: 'pass' | 'fail' | 'warn';
|
|
5
|
+
message: string;
|
|
6
|
+
file?: string;
|
|
7
|
+
line?: number;
|
|
8
|
+
ruleId: string;
|
|
9
|
+
}
|
|
10
|
+
export interface RuleContext {
|
|
11
|
+
config: Ship18ionConfig;
|
|
12
|
+
files: string[];
|
|
13
|
+
cwd: string;
|
|
14
|
+
framework: FrameworkType;
|
|
15
|
+
}
|
|
@@ -12,6 +12,9 @@ const CATEGORIES = {
|
|
|
12
12
|
'security': { icon: '⚠️', label: 'Security' },
|
|
13
13
|
'dep': { icon: '📦', label: 'Dependency & Build' },
|
|
14
14
|
'build': { icon: '📦', label: 'Dependency & Build' },
|
|
15
|
+
'git': { icon: '🐙', label: 'Git & Repo' },
|
|
16
|
+
'hygiene': { icon: '🧹', label: 'Code Hygiene' },
|
|
17
|
+
'package': { icon: '📦', label: 'Packages' },
|
|
15
18
|
};
|
|
16
19
|
function getCategory(ruleId) {
|
|
17
20
|
const prefix = ruleId.split('-')[0];
|
|
@@ -0,0 +1,47 @@
|
|
|
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.checkHygiene = checkHygiene;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
async function checkHygiene(ctx) {
|
|
9
|
+
const results = [];
|
|
10
|
+
const codeFiles = ctx.files.filter(f => f.match(/\.(js|ts|jsx|tsx)$/) &&
|
|
11
|
+
!f.includes('.test.') &&
|
|
12
|
+
!f.includes('.spec.'));
|
|
13
|
+
for (const file of codeFiles) {
|
|
14
|
+
const content = fs_1.default.readFileSync(file, 'utf-8');
|
|
15
|
+
const lines = content.split('\n');
|
|
16
|
+
lines.forEach((line, index) => {
|
|
17
|
+
const lineNum = index + 1;
|
|
18
|
+
// 1. Console Log Check
|
|
19
|
+
// Allow console.error and console.warn, but warn on console.log
|
|
20
|
+
if (line.includes('console.log(')) {
|
|
21
|
+
// Ignore if commented out
|
|
22
|
+
if (!line.trim().startsWith('//') && !line.trim().startsWith('*')) {
|
|
23
|
+
results.push({
|
|
24
|
+
status: 'warn',
|
|
25
|
+
message: 'Leftover console.log() call detected.',
|
|
26
|
+
ruleId: 'hygiene-console-log',
|
|
27
|
+
file,
|
|
28
|
+
line: lineNum
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// 2. TODO / FIXME Check
|
|
33
|
+
if (line.match(/\/\/\s*(TODO|FIXME):/i)) {
|
|
34
|
+
if (line.match(/FIXME/i)) {
|
|
35
|
+
results.push({
|
|
36
|
+
status: 'warn',
|
|
37
|
+
message: 'FIXME comment found. Resolve before shipping.',
|
|
38
|
+
ruleId: 'hygiene-fixme',
|
|
39
|
+
file,
|
|
40
|
+
line: lineNum
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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.checkPackages = checkPackages;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
async function checkPackages(ctx) {
|
|
9
|
+
const results = [];
|
|
10
|
+
const packageJsons = ctx.files.filter(f => f.endsWith('package.json') && !f.includes('node_modules'));
|
|
11
|
+
for (const pkgFile of packageJsons) {
|
|
12
|
+
try {
|
|
13
|
+
const content = JSON.parse(fs_1.default.readFileSync(pkgFile, 'utf-8'));
|
|
14
|
+
const deps = Object.keys(content.dependencies || {});
|
|
15
|
+
const devDeps = Object.keys(content.devDependencies || {});
|
|
16
|
+
// Find intersection
|
|
17
|
+
const duplicates = deps.filter(d => devDeps.includes(d));
|
|
18
|
+
for (const dup of duplicates) {
|
|
19
|
+
results.push({
|
|
20
|
+
status: 'warn',
|
|
21
|
+
message: `Package '${dup}' is listed in both 'dependencies' and 'devDependencies'.`,
|
|
22
|
+
ruleId: 'package-duplicate',
|
|
23
|
+
file: pkgFile
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
// ignore malformed json
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return results;
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ship18ion",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/cli/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ship18ion": "./dist/cli/index.js"
|
|
8
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"CONTRIBUTING.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
9
15
|
"scripts": {
|
|
10
16
|
"build": "tsc",
|
|
11
17
|
"prepublishOnly": "npm run build",
|
|
@@ -21,8 +27,8 @@
|
|
|
21
27
|
"deployment",
|
|
22
28
|
"linter"
|
|
23
29
|
],
|
|
24
|
-
"author": "
|
|
25
|
-
"license": "
|
|
30
|
+
"author": "champ18ion",
|
|
31
|
+
"license": "MIT",
|
|
26
32
|
"dependencies": {
|
|
27
33
|
"@babel/parser": "^7.28.5",
|
|
28
34
|
"@babel/traverse": "^7.28.5",
|
package/SHIPPING.md
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# Shipping ship18ion
|
|
2
|
-
|
|
3
|
-
## 1. Local Testing (Link)
|
|
4
|
-
To test `ship18ion` on your other local projects without publishing, use `npm link`.
|
|
5
|
-
|
|
6
|
-
1. **In this directory (ship18ion):**
|
|
7
|
-
```bash
|
|
8
|
-
npm run build
|
|
9
|
-
npm link
|
|
10
|
-
```
|
|
11
|
-
This creates a global symlink to your local version.
|
|
12
|
-
|
|
13
|
-
2. **In your target project directory:**
|
|
14
|
-
```bash
|
|
15
|
-
npm link ship18ion
|
|
16
|
-
```
|
|
17
|
-
Now you can run:
|
|
18
|
-
```bash
|
|
19
|
-
npx ship18ion check
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
3. **To unlink:**
|
|
23
|
-
```bash
|
|
24
|
-
# In target project
|
|
25
|
-
npm unlink -g ship18ion
|
|
26
|
-
|
|
27
|
-
# In ship18ion directory
|
|
28
|
-
npm unlink -g
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## 2. Publishing to NPM
|
|
32
|
-
To share this tool with the world.
|
|
33
|
-
|
|
34
|
-
1. **Login to NPM:**
|
|
35
|
-
```bash
|
|
36
|
-
npm login
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
2. **Prepare:**
|
|
40
|
-
- Ensure `package.json` has the correct version.
|
|
41
|
-
- Run tests: `npm test` (or verifying script).
|
|
42
|
-
|
|
43
|
-
3. **Publish:**
|
|
44
|
-
```bash
|
|
45
|
-
npm publish
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
If you have a scoped package (e.g. `@yourname/ship18ion`), use:
|
|
49
|
-
```bash
|
|
50
|
-
npm publish --access public
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## 3. Usage for Users
|
|
54
|
-
Once published, anyone can use it without installation:
|
|
55
|
-
```bash
|
|
56
|
-
npx ship18ion@latest
|
|
57
|
-
```
|
package/src/cli/index.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import { loadConfig } from '../engine/config';
|
|
5
|
-
import { runChecks } from '../engine/runner';
|
|
6
|
-
import { reportConsole } from '../reporters/console';
|
|
7
|
-
|
|
8
|
-
const program = new Command();
|
|
9
|
-
|
|
10
|
-
import figlet from 'figlet';
|
|
11
|
-
import gradient from 'gradient-string';
|
|
12
|
-
import ora from 'ora';
|
|
13
|
-
import { detectFramework } from '../engine/detector';
|
|
14
|
-
|
|
15
|
-
program
|
|
16
|
-
.command('check', { isDefault: true })
|
|
17
|
-
.description('Run production readiness checks')
|
|
18
|
-
.option('--ci', 'Run in CI mode (minimal output, exit codes)')
|
|
19
|
-
.action(async (options) => {
|
|
20
|
-
if (!options.ci) {
|
|
21
|
-
console.log(gradient.pastel.multiline(figlet.textSync('SHIP18ION')));
|
|
22
|
-
console.log(chalk.dim('Production Readiness Inspector\n'));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const cwd = process.cwd();
|
|
26
|
-
const config = await loadConfig(cwd);
|
|
27
|
-
const spinner = ora('Initializing...').start();
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
let framework: string = 'unknown';
|
|
31
|
-
if (!options.ci) {
|
|
32
|
-
framework = await detectFramework(cwd);
|
|
33
|
-
spinner.text = `Detected Framework: ${chalk.cyan(framework.toUpperCase())}`;
|
|
34
|
-
await new Promise(r => setTimeout(r, 800)); // Brief pause to show framework
|
|
35
|
-
} else {
|
|
36
|
-
// Even in CI, simple detection is useful for reporting if needed, or we just skip
|
|
37
|
-
framework = await detectFramework(cwd);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const results = await runChecks(config, cwd, (stage) => {
|
|
41
|
-
if (!options.ci) spinner.text = stage;
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
spinner.succeed(chalk.green('Checks completed!'));
|
|
45
|
-
console.log('');
|
|
46
|
-
|
|
47
|
-
// Uses console reporter for both normal and CI for now (it handles exit codes)
|
|
48
|
-
reportConsole(results, cwd, framework);
|
|
49
|
-
} catch (e) {
|
|
50
|
-
spinner.fail(chalk.red('Error running checks'));
|
|
51
|
-
console.error(e);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
program.parse(process.argv);
|
package/src/engine/ast.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import * as parser from '@babel/parser';
|
|
3
|
-
import traverse from '@babel/traverse';
|
|
4
|
-
import * as t from '@babel/types';
|
|
5
|
-
|
|
6
|
-
export interface EnvUsage {
|
|
7
|
-
name: string;
|
|
8
|
-
line: number;
|
|
9
|
-
file: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function findEnvUsages(filePath: string): EnvUsage[] {
|
|
13
|
-
if (!fs.existsSync(filePath)) return [];
|
|
14
|
-
|
|
15
|
-
const code = fs.readFileSync(filePath, 'utf-8');
|
|
16
|
-
const usages: EnvUsage[] = [];
|
|
17
|
-
|
|
18
|
-
// Only parse JS/TS files
|
|
19
|
-
if (!/\.(js|ts|jsx|tsx)$/.test(filePath)) {
|
|
20
|
-
return [];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const ast = parser.parse(code, {
|
|
25
|
-
sourceType: 'module',
|
|
26
|
-
plugins: ['typescript', 'jsx'],
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
traverse(ast, {
|
|
30
|
-
MemberExpression(path) {
|
|
31
|
-
// 1. Check for process.env.VAR
|
|
32
|
-
if (
|
|
33
|
-
t.isMemberExpression(path.node.object) &&
|
|
34
|
-
t.isIdentifier(path.node.object.object) &&
|
|
35
|
-
path.node.object.object.name === 'process' &&
|
|
36
|
-
t.isIdentifier(path.node.object.property) &&
|
|
37
|
-
path.node.object.property.name === 'env'
|
|
38
|
-
) {
|
|
39
|
-
if (t.isIdentifier(path.node.property)) {
|
|
40
|
-
usages.push({
|
|
41
|
-
name: path.node.property.name,
|
|
42
|
-
line: path.node.loc?.start.line || 0,
|
|
43
|
-
file: filePath
|
|
44
|
-
});
|
|
45
|
-
} else if (t.isStringLiteral(path.node.property)) {
|
|
46
|
-
usages.push({
|
|
47
|
-
name: path.node.property.value,
|
|
48
|
-
line: path.node.loc?.start.line || 0,
|
|
49
|
-
file: filePath
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 2. Check for import.meta.env.VAR (Vite)
|
|
55
|
-
// AST structure: MemberExpression
|
|
56
|
-
// object: MemberExpression
|
|
57
|
-
// object: MetaProperty (import.meta)
|
|
58
|
-
// property: Identifier (env)
|
|
59
|
-
// property: Identifier (VAR)
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
t.isMemberExpression(path.node.object) &&
|
|
63
|
-
t.isMetaProperty(path.node.object.object) &&
|
|
64
|
-
path.node.object.object.meta.name === 'import' &&
|
|
65
|
-
path.node.object.object.property.name === 'meta' &&
|
|
66
|
-
t.isIdentifier(path.node.object.property) &&
|
|
67
|
-
path.node.object.property.name === 'env'
|
|
68
|
-
) {
|
|
69
|
-
if (t.isIdentifier(path.node.property)) {
|
|
70
|
-
usages.push({
|
|
71
|
-
name: path.node.property.name,
|
|
72
|
-
line: path.node.loc?.start.line || 0,
|
|
73
|
-
file: filePath
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
} catch (e) {
|
|
81
|
-
// console.warn(`Failed to parse ${filePath}:`, e);
|
|
82
|
-
}
|
|
83
|
-
return usages;
|
|
84
|
-
}
|
package/src/engine/config.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
export interface Ship18ionConfig {
|
|
5
|
-
env?: {
|
|
6
|
-
required?: string[];
|
|
7
|
-
disallowed?: string[];
|
|
8
|
-
};
|
|
9
|
-
security?: {
|
|
10
|
-
noCorsWildcard?: boolean;
|
|
11
|
-
requireRateLimit?: boolean;
|
|
12
|
-
};
|
|
13
|
-
ignore?: string[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function loadConfig(cwd: string = process.cwd()): Promise<Ship18ionConfig> {
|
|
17
|
-
const configPath = path.join(cwd, 'ship18ion.config.json');
|
|
18
|
-
if (fs.existsSync(configPath)) {
|
|
19
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
20
|
-
try {
|
|
21
|
-
return JSON.parse(content);
|
|
22
|
-
} catch (e) {
|
|
23
|
-
console.error('Failed to parse config file:', e);
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return {};
|
|
28
|
-
}
|
package/src/engine/detector.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
export type FrameworkType = 'nextjs' | 'remix' | 'vite' | 'nestjs' | 'express' | 'fastify' | 'Node.js / Generic' | 'unknown';
|
|
5
|
-
|
|
6
|
-
export async function detectFramework(cwd: string): Promise<FrameworkType> {
|
|
7
|
-
const pkgPath = path.join(cwd, 'package.json');
|
|
8
|
-
if (!fs.existsSync(pkgPath)) {
|
|
9
|
-
return 'unknown';
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
14
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
15
|
-
|
|
16
|
-
if (deps['next']) return 'nextjs';
|
|
17
|
-
if (deps['@remix-run/react']) return 'remix';
|
|
18
|
-
if (deps['vite']) return 'vite';
|
|
19
|
-
if (deps['@nestjs/core']) return 'nestjs';
|
|
20
|
-
if (deps['express']) return 'express';
|
|
21
|
-
if (deps['fastify']) return 'fastify';
|
|
22
|
-
|
|
23
|
-
return 'Node.js / Generic';
|
|
24
|
-
} catch (e) {
|
|
25
|
-
return 'unknown';
|
|
26
|
-
}
|
|
27
|
-
}
|