canship 0.4.0 → 0.5.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 CHANGED
@@ -10,9 +10,9 @@ A local static scanner for JavaScript and TypeScript projects. Detects exposed c
10
10
  npx canship .
11
11
  ```
12
12
 
13
- Requires Node.js ≥18; no runtime dependencies. Package installation may use the network. Git checks use local history only; unavailable Git in a repository marks the scan incomplete.
13
+ Requires Node.js ≥18; no runtime dependencies. Installation may use the network. Git checks read local history only; unavailable Git in a repository marks coverage incomplete.
14
14
 
15
- > Documentation for 0.4.0. Earlier [npm versions](https://www.npmjs.com/package/canship) may not include all features below.
15
+ > Documentation for 0.5.0. `npx canship` runs the npm default version; use the corresponding Git tag for other releases.
16
16
 
17
17
  ## Checks
18
18
 
@@ -21,35 +21,30 @@ Requires Node.js ≥18; no runtime dependencies. Package installation may use th
21
21
  | Hardcoded credentials, private keys, and database URLs containing passwords | P0 |
22
22
  | Private values in public environment variables | P0 |
23
23
  | Supabase admin credentials in source or public environment variables | P0 |
24
- | Credentials or suspected private values in Git-tracked and historical `.env` files | P0 |
25
- | Supabase tables without Row Level Security (RLS) in migrations | P1 |
26
- | Supabase RLS policies with always-true conditions | P1 |
24
+ | Git-tracked or historical `.env` files, excluding templates | P0 |
25
+ | Supabase tables without RLS and policies with always-true conditions | P1 |
27
26
  | Public Supabase storage buckets with listable contents | P2 |
28
- | Firebase unconditional access and date-based test rules (Firestore, Storage, Realtime Database) | P1 |
27
+ | Firebase unconditional access and date-based test rules | P1 |
29
28
  | Server-side data operations without recognised authentication | P0 / P1 |
30
29
  | Credentialed CORS with reflected or wildcard origins | P1 / P2 |
31
30
 
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.
31
+ Recognises OpenAI, Anthropic, AWS, Stripe, GitHub, npm, and other credential formats. Firebase checks cover Firestore, Storage, and Realtime Database. Use `--list-rules` for rule IDs and scope.
33
32
 
34
- ### Authentication coverage
33
+ ### Authentication
35
34
 
36
35
  | Framework | Checked entry points |
37
36
  |---|---|
38
- | Next.js | Route handlers under `app/`, Pages Router `/api`, and `'use server'` functions |
37
+ | Next.js | `app/` route handlers, Pages Router `/api`, and `'use server'` functions |
39
38
  | SvelteKit | `+server` endpoints and `+page.server` form actions |
40
39
  | Nuxt | `server/api` and `server/routes` |
41
40
  | Remix / React Router | `loader` and `action` exports in `app/routes` |
42
41
  | Astro | Endpoints in `src/pages` |
43
42
 
44
- Supports route groups and workspace applications. SvelteKit page loads and remote functions are outside scope. Recognised Next.js and Astro middleware guards may suppress covered route findings; Server Functions require a guard within each function. SvelteKit hooks, Nuxt middleware, and local auth helpers can lower confidence without suppressing findings.
43
+ Supports route groups, workspace applications, local helper chains, identity aliases and destructuring, argument requirements, and bounded branch/exception analysis. Raw request input, constants, unawaited promises, or helper names alone do not establish local authentication.
45
44
 
46
- Supabase checks replay local migrations and read supported bucket configuration. Dashboard-only changes and policy conditions implied by missing clauses are not checked.
45
+ Recognised Next.js/Astro middleware may suppress covered findings; Server Functions require function-local checks. Local helpers, SvelteKit hooks, and Nuxt middleware may lower confidence but retain findings. SvelteKit page loads and remote functions are excluded.
47
46
 
48
- ### Confidence and evidence
49
-
50
- `certain` and `likely` describe static evidence, not credential validity or deployed state. Only `certain` findings are shown by default; hidden `likely` findings still affect exit status.
51
-
52
- Admin-client findings include operation, import, and client-construction locations. Supabase constructor aliases and local auth imports, re-exports, and function-returning wrappers are recognised within bounded patterns. Auth resolution follows up to eight hops; evidence chains contain at most 24 steps and disclose truncation. Indirect auth evidence retains the finding at lower confidence; import relationships do not prove runtime data flow.
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.
53
48
 
54
49
  ## CLI
55
50
 
@@ -57,53 +52,84 @@ Omitting the path scans the current directory. Reports are in English.
57
52
 
58
53
  | Option | Effect |
59
54
  |---|---|
60
- | `-a`, `--all` | Include `likely` findings in every format |
55
+ | `-a`, `--all` | Include `likely` findings |
61
56
  | `--json` | Output JSON |
62
57
  | `--fix-prompt` | Output repair instructions and separate manual actions |
63
58
  | `--report[=file]` | Write HTML; default: `canship-report.html` |
64
59
  | `--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 |
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 |
67
62
  | `--only=ids` / `--skip=ids` | Select or exclude rules; comma-separated and repeatable |
68
63
  | `--list-rules` | List rules without scanning; supports `--json` |
69
64
  | `--baseline[=file]` | Suppress recorded findings; default: `canship-baseline.json` |
70
65
  | `--baseline-write[=file]` | Record findings and exit; same default path |
71
66
  | `--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 |
67
+ | `--no-ignore-markers` | Disregard source ignore comments |
68
+ | `--best-effort` | Allow an incomplete scan with no findings to exit `0` |
74
69
  | `-h`, `--help` / `-v`, `--version` | Show help or version |
75
70
 
76
- `--json` and `--fix-prompt` are mutually exclusive; HTML and SARIF can accompany either.
71
+ `--json` and `--fix-prompt` are mutually exclusive. HTML and SARIF can accompany either.
77
72
 
78
73
  ### Exit codes
79
74
 
80
75
  | Code | Meaning |
81
76
  |---|---|
82
- | `0` | No findings; scan complete or incompleteness accepted by `--best-effort` |
77
+ | `0` | No findings; coverage complete or accepted by `--best-effort` |
83
78
  | `1` | At least one `certain` P0/P1 finding |
84
79
  | `2` | Other findings, including hidden `likely` findings |
85
80
  | `3` | Invalid arguments, tool error, or unaccepted incomplete scan |
86
81
 
87
- Counts apply after rule selection, ignore markers, and baselines. Findings take precedence over incompleteness; `--best-effort` does not change `1` or `2`.
82
+ Codes apply after rule selection, ignore markers, and baselines. Findings take precedence over incompleteness; `--best-effort` does not change `1` or `2`.
83
+
84
+ ### Changed-file view and reports
85
+
86
+ `--changed-since=origin/main` compares the local merge base with the working tree, including non-ignored untracked files; it does not fetch. The entire project is scanned. Results are shown when primary or evidence locations changed; repository-wide results and truncated evidence are retained. Hidden results still affect exit status. Missing Git, refs, or shared history exits `3`, even with `--best-effort`. Incompatible with `--baseline-write`.
87
+
88
+ JSON uses `schemaVersion: 1`; fields and filtering counts are defined in the [schema](./schemas/scan-report-v1.schema.json). Check `partial`, `errors`, `skipped`, and `filesScanned` separately from exit status. Allow additive fields; reject unsupported schema versions.
89
+
90
+ SARIF includes execution diagnostics and evidence locations. `--list-rules --json` returns a separate `kind: "rule-catalog"` document.
91
+
92
+ ## Configuration
93
+
94
+ `canship.config.json` in the scan directory accepts `baseline`, `only`, `skip`, and `all`:
95
+
96
+ ```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
88
106
 
89
- ### Changed-file view
107
+ A standalone `canship-ignore-file` comment excludes the file. `canship-ignore-next-line` suppresses the next line, optionally for one rule:
90
108
 
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.
109
+ ```ts
110
+ // canship-ignore-next-line cors/wildcard-with-credentials
111
+ const corsOptions = { origin: '*', credentials: true }
112
+ ```
92
113
 
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`.
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.
94
115
 
95
- ### Structured reports
116
+ ### Baselines
96
117
 
97
- JSON uses `schemaVersion: 1`; the package includes its [schema](./schemas/scan-report-v1.schema.json). Consumers should accept additive fields and reject unsupported schema versions.
118
+ Record existing findings, then suppress them on subsequent scans:
98
119
 
99
- - `findings`: results after suppression and display filtering.
100
- - `hiddenLikely`, `baselineSuppressed`, `baselineStale`: filtering and baseline counts.
101
- - `partial`, `errors`, `skipped`, `filesScanned`: scan coverage; check separately from exit status.
102
- - `changeView`: changed-file filtering counts and full-scan totals, when enabled.
120
+ ```powershell
121
+ npx canship --baseline-write
122
+ ```
103
123
 
104
- SARIF includes execution diagnostics and related evidence locations. `--list-rules --json` returns a separate `kind: "rule-catalog"` document.
124
+ ```powershell
125
+ npx canship --baseline
126
+ ```
127
+
128
+ Default paths are relative to the scan directory; explicit paths are relative to the working directory. Read/write modes are mutually exclusive. A successful write exits `0`, not a clean-scan verdict; incomplete or selective scans produce a warning.
105
129
 
106
- ## Programmatic API
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
107
133
 
108
134
  Node.js ESM with TypeScript declarations:
109
135
 
@@ -115,13 +141,13 @@ console.log(summarize(result))
115
141
  console.log(listRules())
116
142
  ```
117
143
 
118
- `scan()` returns all confidence levels. Options: `only`, `skip`, `honorIgnoreMarkers` (default `true`), `noExcerpts` (default `false`). It does not load project configuration, apply baselines, write reports, or set the process exit code. Invalid arguments or root directories throw; coverage gaps remain in the result.
144
+ `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 exit status. Invalid arguments or roots throw; coverage gaps remain in the result.
119
145
 
120
- `summarize()` returns finding, blocking and likely counts, `partial`, and the default CLI exit code. `listRules()` returns an independent catalog copy.
146
+ `summarize()` returns finding counts, `partial`, and the default CLI exit code. `listRules()` returns an independent catalog copy.
121
147
 
122
148
  ## GitHub Action
123
149
 
124
- Save as `.github/workflows/canship.yml`. The Action installs an exact npm version, scans the checkout, and produces a counts-only summary. It does not install or execute project dependencies; SARIF upload is opt-in.
150
+ Save as `.github/workflows/canship.yml`. The Action installs an exact npm scanner version and writes a counts-only summary. It does not install or run project dependencies; SARIF upload is opt-in.
125
151
 
126
152
  ```yaml
127
153
  name: canship
@@ -136,78 +162,47 @@ jobs:
136
162
  with:
137
163
  fetch-depth: 0
138
164
  persist-credentials: false
139
- - uses: Tasomei/canship@b4cbbfe6b5c4c88164b9388d121f7651032259a4
165
+ - uses: Tasomei/canship@dfc17be52684314c8631d665074c133bf1170888
140
166
  with:
141
- version: '0.4.0'
167
+ version: '0.5.0'
168
+ honor-ignore-markers: false
142
169
  ```
143
170
 
144
- The commit pins the Action wrapper; `version` selects the npm scanner, not repository source. The pinned Action defaults to 0.3.2; this example explicitly selects 0.4.0.
171
+ The commit pins the wrapper; `version` selects the npm scanner, not repository source. This pinned wrapper defaults to 0.4.0.
145
172
 
146
173
  | Input | Default | Meaning |
147
174
  |---|---|---|
148
- | `path` | `.` | Scan directory within the checkout |
149
- | `version` | `0.3.2` | Exact npm version; no ranges or tags |
175
+ | `version` | `0.4.0` | Exact npm version; no ranges or tags |
150
176
  | `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
177
  | `use-config` | `false` | Enable project configuration |
178
+ | `honor-ignore-markers` | `true` | Honour file/line ignore comments |
154
179
  | `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
-
159
- SARIF upload needs `security-events: write` and [code scanning support](https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file); fork PR permissions may be insufficient. Review reports before upload. Use `pull_request`, not `pull_request_target`, for untrusted PRs. The Action sets Node.js 22 for subsequent steps; isolate the scan job if another version is required.
160
-
161
- ## Configuration and baselines
162
-
163
- `canship.config.json` in the scan directory accepts `baseline`, `only`, `skip`, and `all`:
164
180
 
165
- ```json
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:
177
-
178
- ```ts
179
- // canship-ignore-next-line cors/wildcard-with-credentials
180
- const corsOptions = { origin: '*', credentials: true }
181
- ```
182
-
183
- Reports disclose exclusions and suppressions. Deliberate exclusions do not mark the scan incomplete and can reduce the exit code to `0`. `--no-config` does not disable markers; `--no-ignore-markers` does.
184
-
185
- ### Baselines
181
+ Path, rule-selection, baseline, and category inputs are documented in [action.yml](./action.yml).
186
182
 
187
- Record existing findings:
183
+ Outputs: `exit-code`, `findings`, `blocking`, `partial`. Counts include likely findings after suppression. Incomplete scans, tool errors, and incompatible reports fail even with `fail-on: none`.
188
184
 
189
- ```powershell
190
- npx canship --baseline-write
191
- ```
192
-
193
- Suppress them on subsequent scans:
194
-
195
- ```powershell
196
- npx canship --baseline
197
- ```
185
+ SARIF upload requires `security-events: write` and code scanning support; fork PR permissions may be insufficient. Review reports before upload. Use `pull_request`, not `pull_request_target`, for untrusted PRs. The Action sets Node.js 22; use a separate scan job when needed.
198
186
 
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.
187
+ ## Privacy and limits
200
188
 
201
- Format v2 fingerprints survive line moves but change with credentials. Missing, malformed, or v1 baselines exit `3`. Baselines omit source excerpts but retain paths, rules, and issue descriptions; review before committing.
189
+ - Static checks can miss issues or flag intentional configurations. They do not verify deployed behaviour, business authorisation, rate limiting, injection, or dependency vulnerabilities. No findings does not prove security.
190
+ - Redaction covers recognised formats only. Unknown secrets may remain in excerpts; `--no-excerpts` removes excerpts and sets JSON `excerptsOmitted`. Paths, names, descriptions, and baselines are not anonymised.
191
+ - Google/Firebase/Maps `AIza...` keys are treated as public identifiers, not leak evidence alone.
192
+ - Supabase checks use local migrations and supported bucket configuration, not dashboard-only changes or policy conditions implied by omitted clauses.
193
+ - Symbolic links are not followed. Nested repositories and submodules need separate scans. In-scope skipped paths mark coverage incomplete; built-in dependency/build exclusions do not.
202
194
 
203
- ## Privacy and limits
195
+ | Limit | Bound |
196
+ |---|---|
197
+ | File reads, including probes | 2 MiB per file; 128 MiB and 10,000 files per scan |
198
+ | Directory discovery | 50,000 entries; 16 levels |
199
+ | Findings | 100 per file, prioritising severity and confidence |
200
+ | Git history | 100 relevant revisions per file; 30 seconds per Git command |
201
+ | Auth resolution | 8 hops; 128 symbols per route file |
202
+ | Identity/control flow | 8 value hops; 4,000 expression characters; 512 assignments/conditional regions per function; 8 nested branch/exception regions |
203
+ | Supabase policy/bucket parsing | 4,000 characters per statement |
204
204
 
205
- - Static checks can miss issues or report intentional configurations. They do not verify deployed behaviour, rate limiting, injection, dependency vulnerabilities, or business authorisation. No findings does not prove security.
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.
205
+ Exceeded scan/analysis limits report incomplete coverage. Evidence chains are capped at 24 steps and disclose truncation. Getter names and import relationships remain syntactic evidence, not runtime verification.
211
206
 
212
207
  ## Development
213
208
 
@@ -223,26 +218,14 @@ npm run prepublishOnly
223
218
  npm run test:package
224
219
  ```
225
220
 
226
- New rules need positive and negative [fixtures](./test/fixtures/). Run the offline [evaluation corpus](./test/fixtures/evaluation/):
221
+ New rules require positive and negative [fixtures](./test/fixtures/). Run the offline corpus:
227
222
 
228
223
  ```powershell
229
224
  npm run evaluate
230
225
  ```
231
226
 
232
- For [application snapshots](./test/evaluation/projects.json), fetch and verify sources into a new directory outside Git:
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.
227
+ For application evaluation, use the [snapshot manifest](./test/evaluation/projects.json), [fetch script](./scripts/fetch-evaluation-projects.mjs), and [offline evaluator](./scripts/evaluate-projects.ts). Tests compare original and paired variants in temporary copies without running sample dependencies; they do not measure real-world detection rates, Git-history coverage, or deployed behaviour.
245
228
 
246
229
  ## License
247
230
 
248
- [MIT](./LICENSE). Supabase and Firebase fixtures retain Apache-2.0; Next.js and `cors` fixtures retain MIT. Sources and licences accompany the fixtures.
231
+ [MIT](./LICENSE). Supabase/Firebase fixtures retain Apache-2.0; Next.js/`cors` fixtures retain MIT. Sources and licences accompany the fixtures.