canship 0.5.0 → 0.7.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 +94 -127
- package/README.md +94 -127
- package/dist/cli.js +5750 -2164
- package/dist/index.js +4489 -1278
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,153 +1,112 @@
|
|
|
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.7.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. Installation may use the network. Git checks
|
|
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
|
-
| Public Supabase storage buckets with listable contents | P2 |
|
|
27
|
-
| Firebase unconditional access and date-based test rules | P1 |
|
|
28
|
-
| Server-side data operations without recognised authentication | P0 / P1 |
|
|
29
|
-
| Credentialed CORS with reflected or wildcard origins | P1 / P2 |
|
|
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
30
|
|
|
31
|
-
|
|
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
32
|
|
|
33
|
-
###
|
|
33
|
+
### Server entry points
|
|
34
34
|
|
|
35
|
-
| Framework |
|
|
35
|
+
| Framework | Entry points |
|
|
36
36
|
|---|---|
|
|
37
|
-
| Next.js |
|
|
37
|
+
| Next.js | App Router handlers, Pages Router `/api`, `'use server'` functions |
|
|
38
38
|
| SvelteKit | `+server` endpoints and `+page.server` form actions |
|
|
39
|
-
| Nuxt | `server/api
|
|
39
|
+
| Nuxt | `server/api`, `server/routes` |
|
|
40
40
|
| Remix / React Router | `loader` and `action` exports in `app/routes` |
|
|
41
41
|
| Astro | Endpoints in `src/pages` |
|
|
42
|
+
| Express | `app`/`Router` routes, including `.route()` chains, mounted routers, and controllers in other files |
|
|
43
|
+
| Hono | `app.get()`-style routes, chains, `basePath`, and sub-apps mounted with `app.route()` |
|
|
44
|
+
| Fastify | Shorthand and `route()` declarations, `register()` prefixes and encapsulation, `@fastify/autoload` directories |
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
Recognised Next.js/Astro middleware may suppress covered auth findings; Server Functions need local checks. Express, Hono, and Fastify middleware and hooks suppress auth findings only when they resolve to code that rejects unauthenticated requests or to a known auth library; auth-like middleware that cannot be followed lowers confidence. Project session or credential checks (such as `validateSessionToken`) and webhook signature verification (Stripe, Polar, Clerk, Svix, and awaited checks such as QStash `receiver.verify`) count only when the failing branch throws, redirects, or returns 401/403. Local helpers, project wrappers that contain authentication (such as `withWorkspace`), SvelteKit hooks, and Nuxt middleware may lower confidence or mark findings for review without suppressing them. Input analysis follows visible assignments, destructuring, and string construction; helper names alone do not prove sanitisation.
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`certain` and `likely` describe static evidence, not credential validity or runtime security. Default output shows only `certain`; hidden `likely` findings still affect exit status. Admin-client findings include operation, import, construction, and auth-helper locations.
|
|
48
|
-
|
|
49
|
-
## CLI
|
|
48
|
+
For Express, Hono, and Fastify routes, writes inside called project functions are followed two levels (handler → service → model); file-based routes report writes in the route file only. SvelteKit page loads, remote functions, and Hono `app.openapi()` routes are outside route analysis. Content-based checks, including credentials and CORS, still apply.
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
## Results
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|---|---|
|
|
55
|
-
| `-a`, `--all` | Include `likely` findings |
|
|
56
|
-
| `--json` | Output JSON |
|
|
57
|
-
| `--fix-prompt` | Output repair instructions and separate manual actions |
|
|
58
|
-
| `--report[=file]` | Write HTML; default: `canship-report.html` |
|
|
59
|
-
| `--sarif[=file]` | Write SARIF 2.1.0; default: `canship.sarif` |
|
|
60
|
-
| `--no-excerpts` | Omit source excerpts, preserving findings and exit status |
|
|
61
|
-
| `--changed-since=ref` | Show findings related to changed files; retain full-scan exit status |
|
|
62
|
-
| `--only=ids` / `--skip=ids` | Select or exclude rules; comma-separated and repeatable |
|
|
63
|
-
| `--list-rules` | List rules without scanning; supports `--json` |
|
|
64
|
-
| `--baseline[=file]` | Suppress recorded findings; default: `canship-baseline.json` |
|
|
65
|
-
| `--baseline-write[=file]` | Record findings and exit; same default path |
|
|
66
|
-
| `--no-config` | Ignore project configuration |
|
|
67
|
-
| `--no-ignore-markers` | Disregard source ignore comments |
|
|
68
|
-
| `--best-effort` | Allow an incomplete scan with no findings to exit `0` |
|
|
69
|
-
| `-h`, `--help` / `-v`, `--version` | Show help or version |
|
|
52
|
+
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.
|
|
70
53
|
|
|
71
|
-
|
|
54
|
+

|
|
72
55
|
|
|
73
|
-
|
|
56
|
+
`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.
|
|
74
57
|
|
|
75
|
-
|
|
|
58
|
+
| Exit | Meaning |
|
|
76
59
|
|---|---|
|
|
77
|
-
| `0` | No findings; coverage complete or accepted
|
|
60
|
+
| `0` | No findings; coverage complete or accepted with `--best-effort` |
|
|
78
61
|
| `1` | At least one `certain` P0/P1 finding |
|
|
79
|
-
| `2` | Other findings, including hidden `likely`
|
|
80
|
-
| `3` | Invalid arguments, tool error, or unaccepted incomplete
|
|
62
|
+
| `2` | Other findings, including hidden `likely` results |
|
|
63
|
+
| `3` | Invalid arguments, tool error, or unaccepted incomplete coverage |
|
|
64
|
+
|
|
65
|
+
Status is calculated after rule selection, ignore comments, and baselines. Findings take precedence over incomplete coverage; `--best-effort` never changes `1` or `2`.
|
|
81
66
|
|
|
82
|
-
|
|
67
|
+
## CLI
|
|
83
68
|
|
|
84
|
-
|
|
69
|
+
`npx canship [path] [options]`
|
|
85
70
|
|
|
86
|
-
|
|
71
|
+
| Option | Effect |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `-a`, `--all` | Include `likely` findings |
|
|
74
|
+
| `--verbose` | Expand terminal findings |
|
|
75
|
+
| `--report[=file]` | Write HTML; default `canship-report.html` |
|
|
76
|
+
| `--open` | Open `--report` output; disabled in CI and non-interactive shells |
|
|
77
|
+
| `--json` | Print JSON |
|
|
78
|
+
| `--sarif[=file]` | Write SARIF 2.1.0; default `canship.sarif` |
|
|
79
|
+
| `--fix-prompt` | Print repair instructions and separate manual actions |
|
|
80
|
+
| `--no-excerpts` | Remove excerpts from all reports |
|
|
81
|
+
| `--changed-since=ref` | Show changed-file findings; preserve full-scan status |
|
|
82
|
+
| `--only=ids` / `--skip=ids` | Select/exclude rules or namespaces; comma-separated, repeatable |
|
|
83
|
+
| `--list-rules` | List rules without scanning; supports `--json` |
|
|
84
|
+
| `--baseline[=file]` / `--baseline-write[=file]` | Suppress/record findings; default `canship-baseline.json` |
|
|
85
|
+
| `--no-config` / `--no-ignore-markers` | Ignore project configuration/source suppression comments |
|
|
86
|
+
| `--best-effort` | Allow incomplete coverage with no findings to exit `0` |
|
|
87
|
+
| `-h`, `--help` / `-v`, `--version` | Show help/version |
|
|
87
88
|
|
|
88
|
-
|
|
89
|
+
`--json` and `--fix-prompt` are mutually exclusive; either supports HTML and SARIF output.
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
`--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`.
|
|
91
92
|
|
|
92
93
|
## Configuration
|
|
93
94
|
|
|
94
|
-
`canship.config.json`
|
|
95
|
+
`canship.config.json` accepts `baseline`, `only`, `skip`, and `all`. CLI options take precedence; `only` and `skip` are mutually exclusive.
|
|
95
96
|
|
|
96
97
|
```json
|
|
97
|
-
{
|
|
98
|
-
"skip": ["cors/wildcard-with-credentials"],
|
|
99
|
-
"all": false
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
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`.
|
|
104
|
-
|
|
105
|
-
### Ignore comments
|
|
106
|
-
|
|
107
|
-
A standalone `canship-ignore-file` comment excludes the file. `canship-ignore-next-line` suppresses the next line, optionally for one rule:
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
// canship-ignore-next-line cors/wildcard-with-credentials
|
|
111
|
-
const corsOptions = { origin: '*', credentials: true }
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Reports disclose exclusions. Deliberate suppression does not mark coverage incomplete and may reduce the exit code to `0`. `--no-config` does not disable these comments.
|
|
115
|
-
|
|
116
|
-
### Baselines
|
|
117
|
-
|
|
118
|
-
Record existing findings, then suppress them on subsequent scans:
|
|
119
|
-
|
|
120
|
-
```powershell
|
|
121
|
-
npx canship --baseline-write
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
```powershell
|
|
125
|
-
npx canship --baseline
|
|
98
|
+
{ "skip": ["cors/wildcard-with-credentials"], "all": false }
|
|
126
99
|
```
|
|
127
100
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
Format v2 survives line moves but detects credential changes. Missing, malformed, and v1 baselines exit `3`. Baselines omit excerpts but retain paths, rules, and descriptions; review before committing.
|
|
131
|
-
|
|
132
|
-
## API
|
|
133
|
-
|
|
134
|
-
Node.js ESM with TypeScript declarations:
|
|
135
|
-
|
|
136
|
-
```js
|
|
137
|
-
import { scan, summarize, listRules } from 'canship'
|
|
138
|
-
|
|
139
|
-
const result = await scan('./my-app', { noExcerpts: true })
|
|
140
|
-
console.log(summarize(result))
|
|
141
|
-
console.log(listRules())
|
|
142
|
-
```
|
|
101
|
+
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`.
|
|
143
102
|
|
|
144
|
-
|
|
103
|
+
Baselines accept existing findings without fixing them. Format v2 tolerates line moves but reports credential changes.
|
|
145
104
|
|
|
146
|
-
|
|
105
|
+
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.
|
|
147
106
|
|
|
148
107
|
## GitHub Action
|
|
149
108
|
|
|
150
|
-
Save as `.github/workflows/canship.yml
|
|
109
|
+
Save as `.github/workflows/canship.yml`:
|
|
151
110
|
|
|
152
111
|
```yaml
|
|
153
112
|
name: canship
|
|
@@ -162,47 +121,57 @@ jobs:
|
|
|
162
121
|
with:
|
|
163
122
|
fetch-depth: 0
|
|
164
123
|
persist-credentials: false
|
|
165
|
-
- uses: Tasomei/canship@
|
|
124
|
+
- uses: Tasomei/canship@7dfebc9502b786edd5c7fd71266e4926d0ad764b
|
|
166
125
|
with:
|
|
167
|
-
version: '0.
|
|
126
|
+
version: '0.7.0'
|
|
168
127
|
honor-ignore-markers: false
|
|
169
128
|
```
|
|
170
129
|
|
|
171
|
-
The commit pins the
|
|
130
|
+
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.
|
|
172
131
|
|
|
173
132
|
| Input | Default | Meaning |
|
|
174
133
|
|---|---|---|
|
|
175
|
-
| `version` | `0.
|
|
134
|
+
| `version` | `0.6.0` | Exact npm scanner version |
|
|
176
135
|
| `fail-on` | `blocking` | `blocking`: certain P0/P1; `any`: all findings; `none`: report only |
|
|
177
|
-
| `use-config` | `false` | Enable project configuration |
|
|
178
|
-
| `honor-ignore-markers` | `true` | Honour file/line ignore comments |
|
|
179
|
-
| `upload-sarif` | `false` | Upload to GitHub code scanning |
|
|
180
136
|
|
|
181
|
-
|
|
137
|
+
Incomplete scans and tool errors always fail. Project configuration and SARIF upload are disabled by default. Inputs and outputs: [action.yml](./action.yml).
|
|
138
|
+
|
|
139
|
+
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.
|
|
140
|
+
|
|
141
|
+
## API and structured output
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
import { scan, summarize } from 'canship'
|
|
145
|
+
|
|
146
|
+
const result = await scan('./my-app', { noExcerpts: true })
|
|
147
|
+
console.log(summarize(result))
|
|
148
|
+
```
|
|
182
149
|
|
|
183
|
-
|
|
150
|
+
`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
151
|
|
|
185
|
-
|
|
152
|
+
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
153
|
|
|
187
154
|
## Privacy and limits
|
|
188
155
|
|
|
189
|
-
- Static checks
|
|
190
|
-
- Redaction covers recognised formats only. Unknown secrets may remain in excerpts; `--no-excerpts`
|
|
191
|
-
- Google/Firebase/Maps `AIza
|
|
192
|
-
-
|
|
193
|
-
- Symbolic links are not followed
|
|
156
|
+
- Static checks may miss issues or flag intentional configurations. Business authorisation, rate limiting, dependency vulnerabilities, and deployed settings are not verified.
|
|
157
|
+
- Redaction covers recognised formats only. Unknown secrets may remain in excerpts; `--no-excerpts` omits excerpts. Paths, names, and baseline descriptions remain visible.
|
|
158
|
+
- Google/Firebase/Maps `AIza…` keys are treated as public identifiers, not leak evidence alone. Supabase checks use local migrations and supported bucket configuration.
|
|
159
|
+
- Evaluation snapshot downloads and optional SARIF uploads may use the network.
|
|
160
|
+
- Symbolic links are not followed; nested repositories and submodules need separate scans. In-scope skipped paths and analysis limits mark coverage incomplete; auth helper resolution limits are noted on the affected finding instead, because they cannot hide findings. Dependency and build directories excluded by default do not count as coverage gaps.
|
|
194
161
|
|
|
195
162
|
| Limit | Bound |
|
|
196
163
|
|---|---|
|
|
197
|
-
| File reads
|
|
164
|
+
| File reads | 2 MiB per file; 128 MiB and 10,000 files per scan, including probes |
|
|
198
165
|
| Directory discovery | 50,000 entries; 16 levels |
|
|
199
166
|
| Findings | 100 per file, prioritising severity and confidence |
|
|
200
|
-
| Git history | 100 relevant revisions per file; 30 seconds per
|
|
201
|
-
| Auth resolution | 8 hops;
|
|
202
|
-
|
|
|
167
|
+
| Git history | 100 relevant revisions per file; 30 seconds per command |
|
|
168
|
+
| Auth helper resolution | 8 hops; 64 symbols per helper, 1,024 per route file |
|
|
169
|
+
| Delegated writes | 2 call levels; 256 callees per file; writes beyond these limits are not reported |
|
|
170
|
+
| Identity/control flow | 8 value hops; 4,000 expression characters; 512 assignments/regions per function; 8 nested regions |
|
|
171
|
+
| Request-input tracking | 8 value hops; 512 assignments/regions; 64 KiB per expression; 8 URL-analysis levels; 200 static-prefix characters |
|
|
203
172
|
| Supabase policy/bucket parsing | 4,000 characters per statement |
|
|
204
173
|
|
|
205
|
-
|
|
174
|
+
Evidence traces are capped at 24 steps and disclose truncation.
|
|
206
175
|
|
|
207
176
|
## Development
|
|
208
177
|
|
|
@@ -218,14 +187,12 @@ npm run prepublishOnly
|
|
|
218
187
|
npm run test:package
|
|
219
188
|
```
|
|
220
189
|
|
|
221
|
-
New rules require positive and negative [fixtures](./test/fixtures/). Run the offline corpus:
|
|
222
|
-
|
|
223
190
|
```powershell
|
|
224
191
|
npm run evaluate
|
|
225
192
|
```
|
|
226
193
|
|
|
227
|
-
|
|
194
|
+
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.
|
|
228
195
|
|
|
229
196
|
## License
|
|
230
197
|
|
|
231
|
-
[MIT](./LICENSE). Supabase/Firebase fixtures retain Apache-2.0; Next.js/`cors` fixtures retain MIT.
|
|
198
|
+
[MIT](./LICENSE). Supabase/Firebase fixtures retain Apache-2.0; Next.js/`cors` fixtures retain MIT.
|