canship 0.4.0 → 0.6.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-zh-CN.md +99 -153
- package/README.md +97 -151
- package/dist/cli.js +3156 -542
- package/dist/index.js +2434 -195
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,127 +1,109 @@
|
|
|
1
1
|
# canship
|
|
2
2
|
|
|
3
|
-
A local static scanner for JavaScript and TypeScript
|
|
3
|
+
A local static scanner for JavaScript and TypeScript web apps. Checks exposed credentials, access-control configuration, and unsafe request-input flows. No project-code execution, uploads, or network requests during scanning.
|
|
4
4
|
|
|
5
5
|
[简体中文](./README-zh-CN.md)
|
|
6
6
|
|
|
7
|
+
> Documentation for `0.6.0`. Check the installed version with `npx canship --version`.
|
|
8
|
+
|
|
7
9
|
## Quick start
|
|
8
10
|
|
|
9
11
|
```powershell
|
|
10
|
-
npx canship
|
|
12
|
+
npx canship
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
Requires Node.js ≥18; no runtime dependencies.
|
|
15
|
+
Scans the current directory or a specified path. Requires Node.js ≥18; no runtime dependencies. Installation may use the network. Git checks cover locally tracked files and commit history without contacting remotes; inaccessible history marks coverage incomplete.
|
|
16
|
+
|
|
17
|
+
Output examples use sample data from a pre-release development build.
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+

|
|
16
20
|
|
|
17
21
|
## Checks
|
|
18
22
|
|
|
19
|
-
|
|
|
20
|
-
|
|
21
|
-
| Hardcoded
|
|
22
|
-
|
|
|
23
|
-
| Supabase
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Recognises OpenAI, Anthropic, AWS, Stripe, GitHub, npm, and other credential formats, plus common frontend public environment prefixes. Use `--list-rules` for rule IDs, scope, and limits.
|
|
33
|
-
|
|
34
|
-
### Authentication coverage
|
|
35
|
-
|
|
36
|
-
| Framework | Checked entry points |
|
|
23
|
+
| Category | Severity | Scope |
|
|
24
|
+
|---|:---:|---|
|
|
25
|
+
| Credentials | `P0` | Hardcoded keys, private keys, password-bearing database URLs, public env exposure, Supabase admin keys, non-template `.env` files tracked by Git or present in history |
|
|
26
|
+
| API access | `P0/P1` | Database operations without recognised authentication, server-side trust in Supabase `getSession()`, unverified Stripe webhooks |
|
|
27
|
+
| Database rules | `P1/P2` | Supabase tables without RLS, unconditional policies, public object listing in storage buckets; Firebase open rules and time-limited test rules |
|
|
28
|
+
| CORS | `P1/P2` | Reflected or wildcard origins with credentials |
|
|
29
|
+
| Request input | `P1/P2` | Request input in SQL or command construction, caller-chosen request hosts and redirect targets |
|
|
30
|
+
|
|
31
|
+
Credential formats include OpenAI, Anthropic, AWS, Stripe, GitHub, and npm. Firebase covers Firestore, Storage, and Realtime Database. `--list-rules` lists rule IDs, scope, and limitations.
|
|
32
|
+
|
|
33
|
+
### Server entry points
|
|
34
|
+
|
|
35
|
+
| Framework | Entry points |
|
|
37
36
|
|---|---|
|
|
38
|
-
| Next.js |
|
|
37
|
+
| Next.js | App Router handlers, Pages Router `/api`, `'use server'` functions |
|
|
39
38
|
| SvelteKit | `+server` endpoints and `+page.server` form actions |
|
|
40
|
-
| Nuxt | `server/api
|
|
39
|
+
| Nuxt | `server/api`, `server/routes` |
|
|
41
40
|
| Remix / React Router | `loader` and `action` exports in `app/routes` |
|
|
42
41
|
| Astro | Endpoints in `src/pages` |
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Supabase checks replay local migrations and read supported bucket configuration. Dashboard-only changes and policy conditions implied by missing clauses are not checked.
|
|
43
|
+
Recognised Next.js/Astro middleware may suppress covered auth findings; Server Functions need local checks. Local helpers, SvelteKit hooks, and Nuxt middleware may lower confidence without suppressing findings. Input analysis follows visible assignments, destructuring, and string construction; helper names alone do not prove sanitisation.
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
SvelteKit page loads, remote functions, and standalone Express/Hono/Fastify handlers are outside route analysis. Content-based checks, including credentials and CORS, still apply.
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
## Results
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
## CLI
|
|
55
|
-
|
|
56
|
-
Omitting the path scans the current directory. Reports are in English.
|
|
57
|
-
|
|
58
|
-
| Option | Effect |
|
|
59
|
-
|---|---|
|
|
60
|
-
| `-a`, `--all` | Include `likely` findings in every format |
|
|
61
|
-
| `--json` | Output JSON |
|
|
62
|
-
| `--fix-prompt` | Output repair instructions and separate manual actions |
|
|
63
|
-
| `--report[=file]` | Write HTML; default: `canship-report.html` |
|
|
64
|
-
| `--sarif[=file]` | Write SARIF 2.1.0; default: `canship.sarif` |
|
|
65
|
-
| `--no-excerpts` | Omit source excerpts; preserve findings and exit status |
|
|
66
|
-
| `--changed-since=ref` | Filter the report by changed files, not the scan or exit status |
|
|
67
|
-
| `--only=ids` / `--skip=ids` | Select or exclude rules; comma-separated and repeatable |
|
|
68
|
-
| `--list-rules` | List rules without scanning; supports `--json` |
|
|
69
|
-
| `--baseline[=file]` | Suppress recorded findings; default: `canship-baseline.json` |
|
|
70
|
-
| `--baseline-write[=file]` | Record findings and exit; same default path |
|
|
71
|
-
| `--no-config` | Ignore project configuration |
|
|
72
|
-
| `--no-ignore-markers` | Disregard source ignore markers |
|
|
73
|
-
| `--best-effort` | Allow exit `0` for an incomplete scan with no findings |
|
|
74
|
-
| `-h`, `--help` / `-v`, `--version` | Show help or version |
|
|
49
|
+
Reports are in English. The terminal groups findings by file; `--verbose` adds excerpts, explanations, evidence, and fixes. HTML is a self-contained offline report with filters, grouping, manual steps, and copyable fix prompts.
|
|
75
50
|
|
|
76
|
-
|
|
51
|
+

|
|
77
52
|
|
|
78
|
-
|
|
53
|
+
`certain` indicates strong static evidence; `likely` requires review. Findings in tests and examples are downgraded to `likely`. Default output shows only `certain`; `--all` includes both. Confidence reflects static evidence, not credential validity or runtime verification.
|
|
79
54
|
|
|
80
|
-
|
|
|
55
|
+
| Exit | Meaning |
|
|
81
56
|
|---|---|
|
|
82
|
-
| `0` | No findings;
|
|
57
|
+
| `0` | No findings; coverage complete or accepted with `--best-effort` |
|
|
83
58
|
| `1` | At least one `certain` P0/P1 finding |
|
|
84
|
-
| `2` | Other findings, including hidden `likely`
|
|
85
|
-
| `3` | Invalid arguments, tool error, or unaccepted incomplete
|
|
59
|
+
| `2` | Other findings, including hidden `likely` results |
|
|
60
|
+
| `3` | Invalid arguments, tool error, or unaccepted incomplete coverage |
|
|
86
61
|
|
|
87
|
-
|
|
62
|
+
Status is calculated after rule selection, ignore comments, and baselines. Findings take precedence over incomplete coverage; `--best-effort` never changes `1` or `2`.
|
|
88
63
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
`--changed-since=origin/main` compares the local merge base with the working tree, including non-ignored untracked files. It does not fetch. The whole project is still scanned; findings are shown when their primary or evidence locations changed. Repository-wide findings and truncated evidence are retained.
|
|
92
|
-
|
|
93
|
-
Hidden findings still affect exit status: this is a review view, not a “new issues only” CI policy. Missing Git, refs, or merge history exits `3`, even with `--best-effort`. Cannot be combined with `--baseline-write`.
|
|
94
|
-
|
|
95
|
-
### Structured reports
|
|
64
|
+
## CLI
|
|
96
65
|
|
|
97
|
-
|
|
66
|
+
`npx canship [path] [options]`
|
|
98
67
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
68
|
+
| Option | Effect |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `-a`, `--all` | Include `likely` findings |
|
|
71
|
+
| `--verbose` | Expand terminal findings |
|
|
72
|
+
| `--report[=file]` | Write HTML; default `canship-report.html` |
|
|
73
|
+
| `--open` | Open `--report` output; disabled in CI and non-interactive shells |
|
|
74
|
+
| `--json` | Print JSON |
|
|
75
|
+
| `--sarif[=file]` | Write SARIF 2.1.0; default `canship.sarif` |
|
|
76
|
+
| `--fix-prompt` | Print repair instructions and separate manual actions |
|
|
77
|
+
| `--no-excerpts` | Remove excerpts from all reports |
|
|
78
|
+
| `--changed-since=ref` | Show changed-file findings; preserve full-scan status |
|
|
79
|
+
| `--only=ids` / `--skip=ids` | Select/exclude rules or namespaces; comma-separated, repeatable |
|
|
80
|
+
| `--list-rules` | List rules without scanning; supports `--json` |
|
|
81
|
+
| `--baseline[=file]` / `--baseline-write[=file]` | Suppress/record findings; default `canship-baseline.json` |
|
|
82
|
+
| `--no-config` / `--no-ignore-markers` | Ignore project configuration/source suppression comments |
|
|
83
|
+
| `--best-effort` | Allow incomplete coverage with no findings to exit `0` |
|
|
84
|
+
| `-h`, `--help` / `-v`, `--version` | Show help/version |
|
|
103
85
|
|
|
104
|
-
|
|
86
|
+
`--json` and `--fix-prompt` are mutually exclusive; either supports HTML and SARIF output.
|
|
105
87
|
|
|
106
|
-
|
|
88
|
+
`--changed-since` compares the local merge base with the working tree, including non-ignored untracked files. It does not fetch or narrow scan scope. Missing Git, refs, or shared history exits `3`; it cannot be combined with `--baseline-write`.
|
|
107
89
|
|
|
108
|
-
|
|
90
|
+
## Configuration
|
|
109
91
|
|
|
110
|
-
|
|
111
|
-
import { scan, summarize, listRules } from 'canship'
|
|
92
|
+
`canship.config.json` accepts `baseline`, `only`, `skip`, and `all`. CLI options take precedence; `only` and `skip` are mutually exclusive.
|
|
112
93
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
console.log(listRules())
|
|
94
|
+
```json
|
|
95
|
+
{ "skip": ["cors/wildcard-with-credentials"], "all": false }
|
|
116
96
|
```
|
|
117
97
|
|
|
118
|
-
`
|
|
98
|
+
A standalone `canship-ignore-file` comment excludes a file. `canship-ignore-next-line [rule]` suppresses the next line, optionally for one rule. Exclusions are disclosed and may reduce status to `0` without marking coverage incomplete. For untrusted projects, use `--no-config --no-ignore-markers`.
|
|
119
99
|
|
|
120
|
-
|
|
100
|
+
Baselines accept existing findings without fixing them. Format v2 tolerates line moves but reports credential changes.
|
|
101
|
+
|
|
102
|
+
Default paths are relative to the scan directory; explicit paths are relative to the working directory. Read/write modes are mutually exclusive. Missing, invalid, or v1 baselines exit `3`. A successful write exits `0`, with a warning for incomplete or selective scans.
|
|
121
103
|
|
|
122
104
|
## GitHub Action
|
|
123
105
|
|
|
124
|
-
Save as `.github/workflows/canship.yml
|
|
106
|
+
Save as `.github/workflows/canship.yml`:
|
|
125
107
|
|
|
126
108
|
```yaml
|
|
127
109
|
name: canship
|
|
@@ -136,78 +118,56 @@ jobs:
|
|
|
136
118
|
with:
|
|
137
119
|
fetch-depth: 0
|
|
138
120
|
persist-credentials: false
|
|
139
|
-
- uses: Tasomei/canship@
|
|
121
|
+
- uses: Tasomei/canship@97c14d1f1e494a49adf716c455b597edf6ae1d88
|
|
140
122
|
with:
|
|
141
|
-
version: '0.
|
|
123
|
+
version: '0.6.0'
|
|
124
|
+
honor-ignore-markers: false
|
|
142
125
|
```
|
|
143
126
|
|
|
144
|
-
The commit pins the Action
|
|
127
|
+
The commit hash pins the Action implementation; `version` selects the npm scanner, not development-branch source. The Action uses Node.js 22, does not install or run project dependencies, and writes a counts-only summary.
|
|
145
128
|
|
|
146
129
|
| Input | Default | Meaning |
|
|
147
130
|
|---|---|---|
|
|
148
|
-
| `
|
|
149
|
-
| `version` | `0.3.2` | Exact npm version; no ranges or tags |
|
|
131
|
+
| `version` | `0.5.0` | Exact npm scanner version |
|
|
150
132
|
| `fail-on` | `blocking` | `blocking`: certain P0/P1; `any`: all findings; `none`: report only |
|
|
151
|
-
| `only` / `skip` | unset | Mutually exclusive rule selectors |
|
|
152
|
-
| `baseline` | unset | Existing baseline relative to the scan directory |
|
|
153
|
-
| `use-config` | `false` | Enable project configuration |
|
|
154
|
-
| `upload-sarif` | `false` | Upload to GitHub code scanning |
|
|
155
|
-
| `category` | `canship` | SARIF category for the scan target |
|
|
156
|
-
|
|
157
|
-
Outputs: `exit-code`, `findings`, `blocking`, `partial`. Counts include likely findings after baselines and exclusions. Incomplete scans, tool errors, and incompatible reports always fail, even with `fail-on: none`.
|
|
158
133
|
|
|
159
|
-
|
|
134
|
+
Incomplete scans and tool errors always fail. Project configuration and SARIF upload are disabled by default. Inputs and outputs: [action.yml](./action.yml).
|
|
160
135
|
|
|
161
|
-
|
|
136
|
+
SARIF upload requires `security-events: write` and code scanning support; fork PRs may lack permission. Review reports before upload. Use `pull_request`, not `pull_request_target`, for untrusted PRs.
|
|
162
137
|
|
|
163
|
-
|
|
138
|
+
## API and structured output
|
|
164
139
|
|
|
165
|
-
```
|
|
166
|
-
{
|
|
167
|
-
"skip": ["cors/wildcard-with-credentials"],
|
|
168
|
-
"all": false
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
CLI options take precedence. `only` and `skip` are mutually exclusive and accept rule IDs or namespaces. `--best-effort` is CLI-only. For untrusted projects, use `--no-config --no-ignore-markers`.
|
|
173
|
-
|
|
174
|
-
### Ignore markers
|
|
175
|
-
|
|
176
|
-
A standalone `canship-ignore-file` comment excludes the file. `canship-ignore-next-line` suppresses the next line, optionally for one rule:
|
|
140
|
+
```js
|
|
141
|
+
import { scan, summarize } from 'canship'
|
|
177
142
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const corsOptions = { origin: '*', credentials: true }
|
|
143
|
+
const result = await scan('./my-app', { noExcerpts: true })
|
|
144
|
+
console.log(summarize(result))
|
|
181
145
|
```
|
|
182
146
|
|
|
183
|
-
|
|
147
|
+
`scan()` returns all confidence levels. Options: `only`, `skip`, `honorIgnoreMarkers` (default `true`), `noExcerpts` (default `false`). It does not load configuration, apply baselines, write reports, or set process exit status. Invalid arguments throw. `listRules()` returns the rule catalogue.
|
|
184
148
|
|
|
185
|
-
|
|
149
|
+
JSON uses [schemaVersion 1](./schemas/scan-report-v1.schema.json). Check `partial`, `errors`, `skipped`, and `filesScanned` independently of exit status. SARIF includes evidence locations and execution diagnostics.
|
|
186
150
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
```powershell
|
|
190
|
-
npx canship --baseline-write
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Suppress them on subsequent scans:
|
|
194
|
-
|
|
195
|
-
```powershell
|
|
196
|
-
npx canship --baseline
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
The default path is relative to the scan directory; explicit paths are relative to the working directory. Read and write modes are mutually exclusive. Successful writes exit `0` regardless of findings; incomplete or selective scans produce a warning.
|
|
151
|
+
## Privacy and limits
|
|
200
152
|
|
|
201
|
-
|
|
153
|
+
- Static checks may miss issues or flag intentional configurations. Business authorisation, rate limiting, dependency vulnerabilities, and deployed settings are not verified.
|
|
154
|
+
- Redaction covers recognised formats only. Unknown secrets may remain in excerpts; `--no-excerpts` omits excerpts. Paths, names, and baseline descriptions remain visible.
|
|
155
|
+
- Google/Firebase/Maps `AIza…` keys are treated as public identifiers, not leak evidence alone. Supabase checks use local migrations and supported bucket configuration.
|
|
156
|
+
- Evaluation snapshot downloads and optional SARIF uploads may use the network.
|
|
157
|
+
- Symbolic links are not followed; nested repositories and submodules need separate scans. In-scope skipped paths and analysis limits mark coverage incomplete. Dependency and build directories excluded by default do not count as coverage gaps.
|
|
202
158
|
|
|
203
|
-
|
|
159
|
+
| Limit | Bound |
|
|
160
|
+
|---|---|
|
|
161
|
+
| File reads | 2 MiB per file; 128 MiB and 10,000 files per scan, including probes |
|
|
162
|
+
| Directory discovery | 50,000 entries; 16 levels |
|
|
163
|
+
| Findings | 100 per file, prioritising severity and confidence |
|
|
164
|
+
| Git history | 100 relevant revisions per file; 30 seconds per command |
|
|
165
|
+
| Auth resolution | 8 hops; 128 symbols per route file |
|
|
166
|
+
| Identity/control flow | 8 value hops; 4,000 expression characters; 512 assignments/regions per function; 8 nested regions |
|
|
167
|
+
| Request-input tracking | 8 value hops; 512 assignments/regions; 4,000 expression characters; 8 URL-analysis levels; 200 static-prefix characters |
|
|
168
|
+
| Supabase policy/bucket parsing | 4,000 characters per statement |
|
|
204
169
|
|
|
205
|
-
|
|
206
|
-
- Redaction covers recognised formats only. Unknown secrets may remain in excerpts; `--no-excerpts` omits excerpts and sets JSON `excerptsOmitted`. Paths, names, descriptions, and baselines are not anonymised.
|
|
207
|
-
- Google/Firebase/Maps `AIza...` keys are treated as public identifiers, not leak evidence on their own.
|
|
208
|
-
- Read limits: 2 MiB per file, 128 MiB and 10,000 files per scan, 16 directory levels. At most 100 findings per file across rules, prioritising severity and confidence.
|
|
209
|
-
- Git history: up to 100 relevant revisions per file; 30-second timeout per Git command. Supabase policy and bucket statements: 4,000-character parse limit. Exceeded limits report incomplete coverage.
|
|
210
|
-
- Symbolic links are not followed; nested repositories and submodules need separate scans. Skipped in-scope paths mark coverage incomplete; built-in dependency and build exclusions do not.
|
|
170
|
+
Evidence traces are capped at 24 steps and disclose truncation.
|
|
211
171
|
|
|
212
172
|
## Development
|
|
213
173
|
|
|
@@ -223,26 +183,12 @@ npm run prepublishOnly
|
|
|
223
183
|
npm run test:package
|
|
224
184
|
```
|
|
225
185
|
|
|
226
|
-
New rules need positive and negative [fixtures](./test/fixtures/). Run the offline [evaluation corpus](./test/fixtures/evaluation/):
|
|
227
|
-
|
|
228
186
|
```powershell
|
|
229
187
|
npm run evaluate
|
|
230
188
|
```
|
|
231
189
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
```powershell
|
|
235
|
-
node scripts/fetch-evaluation-projects.mjs "$env:TEMP/canship-evaluation"
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
Then evaluate offline:
|
|
239
|
-
|
|
240
|
-
```powershell
|
|
241
|
-
npm run evaluate:projects -- "$env:TEMP/canship-evaluation"
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
Project evaluation compares all findings in original and open/guarded test variants using temporary copies. Sample dependencies are not installed or run. These tests do not measure real-world detection rates, Git-history coverage, or deployed behaviour.
|
|
190
|
+
New rules require positive and negative [fixtures](./test/fixtures/). Pinned project evaluation: [manifest](./test/evaluation/projects.json), [fetch script](./scripts/fetch-evaluation-projects.mjs), [evaluator](./scripts/evaluate-projects.ts). Passing samples do not establish real-world detection rates.
|
|
245
191
|
|
|
246
192
|
## License
|
|
247
193
|
|
|
248
|
-
[MIT](./LICENSE). Supabase
|
|
194
|
+
[MIT](./LICENSE). Supabase/Firebase fixtures retain Apache-2.0; Next.js/`cors` fixtures retain MIT.
|