canship 0.3.2 → 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
@@ -1,16 +1,18 @@
1
1
  # canship
2
2
 
3
- A local static scanner for JavaScript and TypeScript projects. Detects exposed credentials and access-control misconfigurations. Scans do not execute project code, upload files, or use the network.
3
+ A local static scanner for JavaScript and TypeScript projects. Detects exposed credentials and access-control misconfigurations without executing project code, uploading files, or making network requests.
4
4
 
5
- This documentation covers 0.3.x: use a matching [npm version](https://www.npmjs.com/package/canship) or local build.
5
+ [简体中文](./README-zh-CN.md)
6
+
7
+ ## Quick start
6
8
 
7
9
  ```powershell
8
10
  npx canship .
9
11
  ```
10
12
 
11
- Requires Node.js ≥18; no runtime dependencies. `npx` may download the package; scans use only local files and Git history. 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.
12
14
 
13
- [简体中文](./README-zh-CN.md)
15
+ > Documentation for 0.5.0. `npx canship` runs the npm default version; use the corresponding Git tag for other releases.
14
16
 
15
17
  ## Checks
16
18
 
@@ -18,101 +20,78 @@ Requires Node.js ≥18; no runtime dependencies. `npx` may download the package;
18
20
  |---|---|
19
21
  | Hardcoded credentials, private keys, and database URLs containing passwords | P0 |
20
22
  | Private values in public environment variables | P0 |
21
- | Supabase admin keys exposed to clients | P0 |
22
- | Credentials or suspected private values in Git-tracked and historical `.env` files | P0 |
23
- | Supabase tables without Row Level Security (RLS) in migrations | P1 |
23
+ | Supabase admin credentials in source or public environment variables | P0 |
24
+ | Git-tracked or historical `.env` files, excluding templates | P0 |
25
+ | Supabase tables without RLS and policies with always-true conditions | P1 |
26
+ | Public Supabase storage buckets with listable contents | P2 |
24
27
  | Firebase unconditional access and date-based test rules | P1 |
25
- | Server route data operations without recognised authentication | P0 / P1 |
28
+ | Server-side data operations without recognised authentication | P0 / P1 |
26
29
  | Credentialed CORS with reflected or wildcard origins | P1 / P2 |
27
30
 
28
- Supports OpenAI, Anthropic, AWS, Stripe, GitHub, npm, Slack, SendGrid, and other credential formats, plus common frontend public environment prefixes. API authentication checks cover server routes in Next.js (`/api` in the App and Pages Router), SvelteKit (`+server` endpoints), Nuxt (`server/api` and `server/routes`), Remix and React Router (modules in `app/routes` exporting `loader` or `action`), and Astro (endpoints in `src/pages`), including route groups and workspace applications. SvelteKit page loads and form actions are not checked. An authentication check in SvelteKit `hooks.server` or Nuxt `server/middleware` lowers confidence instead of suppressing findings, because the routes it covers are decided in code.
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.
32
+
33
+ ### Authentication
34
+
35
+ | Framework | Checked entry points |
36
+ |---|---|
37
+ | Next.js | `app/` route handlers, Pages Router `/api`, and `'use server'` functions |
38
+ | SvelteKit | `+server` endpoints and `+page.server` form actions |
39
+ | Nuxt | `server/api` and `server/routes` |
40
+ | Remix / React Router | `loader` and `action` exports in `app/routes` |
41
+ | Astro | Endpoints in `src/pages` |
42
+
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.
44
+
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.
29
46
 
30
- Confidence is `certain` or `likely`, describing static evidence rather than credential validity or deployed state. Only certain findings are shown by default; hidden likely findings still affect the exit code.
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.
31
48
 
32
- ## Usage
49
+ ## CLI
33
50
 
34
- Omitting the path scans the current directory.
51
+ Omitting the path scans the current directory. Reports are in English.
35
52
 
36
- | Option | Description |
53
+ | Option | Effect |
37
54
  |---|---|
38
- | `-a`, `--all` | Include likely findings |
55
+ | `-a`, `--all` | Include `likely` findings |
39
56
  | `--json` | Output JSON |
40
- | `--fix-prompt` | Output remediation instructions and separate manual actions |
57
+ | `--fix-prompt` | Output repair instructions and separate manual actions |
41
58
  | `--report[=file]` | Write HTML; default: `canship-report.html` |
42
59
  | `--sarif[=file]` | Write SARIF 2.1.0; default: `canship.sarif` |
43
- | `--best-effort` | Permit exit `0` for an incomplete scan with no findings |
44
- | `--baseline[=file]` | Apply a baseline; default: `canship-baseline.json` |
45
- | `--baseline-write[=file]` | Write current findings as a baseline and exit; same default path |
46
- | `--only=ids` | Run matching rules; comma-separated and repeatable |
47
- | `--skip=ids` | Exclude matching rules; comma-separated and repeatable |
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 |
48
66
  | `--no-config` | Ignore project configuration |
49
- | `--no-ignore-markers` | Disregard ignore markers in scanned source |
50
- | `--list-rules` | List rules and limits without scanning; supports `--json` |
51
- | `--no-excerpts` | Omit source excerpts from every report; preserve findings and exit status |
52
- | `-h`, `--help` | Show help |
53
- | `-v`, `--version` | Show version |
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 |
54
70
 
55
- `--json` and `--fix-prompt` are mutually exclusive; HTML and SARIF work with either. Reports are in English. `--all` applies to every format.
71
+ `--json` and `--fix-prompt` are mutually exclusive. HTML and SARIF can accompany either.
56
72
 
57
73
  ### Exit codes
58
74
 
59
75
  | Code | Meaning |
60
76
  |---|---|
61
- | `0` | No findings, with a complete scan or an incomplete scan accepted by `--best-effort` |
62
- | `1` | At least one certain P0/P1 finding |
63
- | `2` | Other findings, including hidden likely findings |
64
- | `3` | Invalid arguments, a tool error, or an unaccepted incomplete scan |
65
-
66
- Finding exit codes take precedence over incompleteness; `--best-effort` does not change `1` or `2`.
67
-
68
- ### Machine-readable output
77
+ | `0` | No findings; coverage complete or accepted by `--best-effort` |
78
+ | `1` | At least one `certain` P0/P1 finding |
79
+ | `2` | Other findings, including hidden `likely` findings |
80
+ | `3` | Invalid arguments, tool error, or unaccepted incomplete scan |
69
81
 
70
- JSON uses `schemaVersion: 1`, independent of the package version; the npm package includes its [schema](./schemas/scan-report-v1.schema.json). Accept additive fields and reject unsupported schema versions. `--list-rules --json` is a separate `kind: "rule-catalog"` document.
82
+ Codes apply after rule selection, ignore markers, and baselines. Findings take precedence over incompleteness; `--best-effort` does not change `1` or `2`.
71
83
 
72
- `findings` contains results after suppressions and filtering; `hiddenLikely`, `baselineSuppressed`, and `baselineStale` provide related counts. Check coverage separately through `partial`, `errors`, `skipped`, and `filesScanned`. SARIF includes execution status and diagnostic notifications.
73
-
74
- ## GitHub Action
75
-
76
- Save as `.github/workflows/canship.yml` to scan on pushes and pull requests, with a counts-only summary. Scanner installation requires network access; scanning does not. Project dependencies are not installed or executed, and SARIF upload is disabled by default.
77
-
78
- The example pins the Action commit and explicitly installs npm version `0.3.1`; `version` does not use unreleased repository source. The Action accepts 0.2.1 reports without `schemaVersion`.
79
-
80
- ```yaml
81
- name: canship
82
- on: [push, pull_request]
83
- permissions:
84
- contents: read
85
- jobs:
86
- scan:
87
- runs-on: ubuntu-latest
88
- steps:
89
- - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
90
- with:
91
- fetch-depth: 0
92
- persist-credentials: false
93
- - uses: Tasomei/canship@2d33cce0ad8439e34f01f5218fdaaf4657215e3d
94
- with:
95
- version: '0.3.1'
96
- ```
84
+ ### Changed-file view and reports
97
85
 
98
- | Input | Default | Meaning |
99
- |---|---|---|
100
- | `path` | `.` | Directory within the checkout |
101
- | `version` | `0.3.1` | Exact npm version; no ranges or tags |
102
- | `fail-on` | `blocking` | `blocking`: certain P0/P1; `any`: all findings; `none`: findings only reported |
103
- | `only` / `skip` | unset | Mutually exclusive, comma-separated rule selectors |
104
- | `baseline` | unset | Existing baseline relative to the scanned directory |
105
- | `use-config` | `false` | Enable project configuration |
106
- | `upload-sarif` | `false` | Upload SARIF to GitHub code scanning |
107
- | `category` | `canship` | Distinct SARIF category for each scan target |
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`.
108
87
 
109
- Outputs: `exit-code`, `findings`, `blocking`, `partial`. Policies include likely findings; baselines and exclusions still apply. Incomplete scans, tool errors, and incompatible reports always fail, including with `fail-on: none`.
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.
110
89
 
111
- SARIF upload requires `security-events: write` and [GitHub 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 PRs may lack permission. Review paths and finding details before upload. Use `pull_request`, not `pull_request_target`, for untrusted PRs. The Action sets Node.js 22 for subsequent steps; use a separate scan job if another version is needed.
90
+ SARIF includes execution diagnostics and evidence locations. `--list-rules --json` returns a separate `kind: "rule-catalog"` document.
112
91
 
113
- ## Configuration and baselines
92
+ ## Configuration
114
93
 
115
- Place `canship.config.json` in the scanned directory. Supported keys: `baseline`, `only`, `skip`, `all`.
94
+ `canship.config.json` in the scan directory accepts `baseline`, `only`, `skip`, and `all`:
116
95
 
117
96
  ```json
118
97
  {
@@ -121,80 +100,132 @@ Place `canship.config.json` in the scanned directory. Supported keys: `baseline`
121
100
  }
122
101
  ```
123
102
 
124
- CLI options take precedence. `only` and `skip` are mutually exclusive and accept rule IDs or namespaces. Unselected rules do not run; `ruleSelection.removed` counts filtered findings only from executed rules. For untrusted projects use `--no-config --no-ignore-markers`; the scanned project controls both. `bestEffort` is CLI-only.
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`.
125
104
 
126
- ### Ignore markers
105
+ ### Ignore comments
127
106
 
128
- A standalone `canship-ignore-file` comment excludes a file; `canship-ignore-next-line` suppresses the next line, with an optional rule ID:
107
+ A standalone `canship-ignore-file` comment excludes the file. `canship-ignore-next-line` suppresses the next line, optionally for one rule:
129
108
 
130
109
  ```ts
131
110
  // canship-ignore-next-line cors/wildcard-with-credentials
132
111
  const corsOptions = { origin: '*', credentials: true }
133
112
  ```
134
113
 
135
- Reports disclose exclusions, rule selection, and baseline suppression. Deliberate exclusions do not mark the scan incomplete. Markers can lower the exit code to `0`; `--no-config` does not affect them, `--no-ignore-markers` disables both kinds.
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.
136
115
 
137
116
  ### Baselines
138
117
 
139
- Record existing findings:
118
+ Record existing findings, then suppress them on subsequent scans:
140
119
 
141
120
  ```powershell
142
121
  npx canship --baseline-write
143
122
  ```
144
123
 
145
- Report only new findings:
146
-
147
124
  ```powershell
148
125
  npx canship --baseline
149
126
  ```
150
127
 
151
- The default baseline is in the scanned directory; explicit paths are relative to the working directory. A successful write exits `0`, regardless of findings; incomplete or selectively scanned input produces a warning.
152
-
153
- Baseline format v2 fingerprints survive line moves but change when credentials change. Missing, malformed, and v1 baselines exit `3`. Baselines omit source but retain paths, rules, and issue descriptions; review before committing.
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.
154
129
 
155
- ## Privacy and limitations
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.
156
131
 
157
- - Static analysis may produce false positives or negatives. Deployed behaviour, rate limiting, injection, dependency vulnerabilities, and business authorisation are outside scope. No findings does not prove security.
158
- - Redaction covers recognised formats only; unknown secrets may appear in source excerpts. `--no-excerpts` omits excerpts, recorded as `excerptsOmitted` in JSON. Paths, names, descriptions, and baselines are not anonymised; review before sharing.
159
- - Google/Firebase/Maps `AIza...` values are public identifiers, not evidence of a leak on their own.
160
- - 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.
161
- - Git history checks cover up to 100 relevant revisions per file, with a 30-second timeout per Git command. Exceeded limits and timeouts report coverage gaps.
162
- - Symbolic links are not followed; scan nested repositories and submodules separately. Skipped paths within scope mark the scan incomplete; built-in build and dependency exclusions do not.
132
+ ## API
163
133
 
164
- ## Development
134
+ Node.js ESM with TypeScript declarations:
165
135
 
166
- New rules require positive and negative [test cases](./test/fixtures/).
136
+ ```js
137
+ import { scan, summarize, listRules } from 'canship'
167
138
 
168
- ```powershell
169
- npm ci
139
+ const result = await scan('./my-app', { noExcerpts: true })
140
+ console.log(summarize(result))
141
+ console.log(listRules())
170
142
  ```
171
143
 
172
- ```powershell
173
- npm run prepublishOnly
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.
145
+
146
+ `summarize()` returns finding counts, `partial`, and the default CLI exit code. `listRules()` returns an independent catalog copy.
147
+
148
+ ## GitHub Action
149
+
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.
151
+
152
+ ```yaml
153
+ name: canship
154
+ on: [push, pull_request]
155
+ permissions:
156
+ contents: read
157
+ jobs:
158
+ scan:
159
+ runs-on: ubuntu-latest
160
+ steps:
161
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
162
+ with:
163
+ fetch-depth: 0
164
+ persist-credentials: false
165
+ - uses: Tasomei/canship@dfc17be52684314c8631d665074c133bf1170888
166
+ with:
167
+ version: '0.5.0'
168
+ honor-ignore-markers: false
174
169
  ```
175
170
 
176
- Offline evaluation:
171
+ The commit pins the wrapper; `version` selects the npm scanner, not repository source. This pinned wrapper defaults to 0.4.0.
172
+
173
+ | Input | Default | Meaning |
174
+ |---|---|---|
175
+ | `version` | `0.4.0` | Exact npm version; no ranges or tags |
176
+ | `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
+
181
+ Path, rule-selection, baseline, and category inputs are documented in [action.yml](./action.yml).
182
+
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`.
184
+
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.
186
+
187
+ ## Privacy and limits
188
+
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.
194
+
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
+
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.
206
+
207
+ ## Development
177
208
 
178
209
  ```powershell
179
- npm run evaluate
210
+ npm ci
180
211
  ```
181
212
 
182
- The corpus has 10 synthetic cases and nine pinned upstream examples and mutations, also run by `npm test`. [Sources and licences](./test/fixtures/evaluation/) accompany the fixtures. Assertions cover rules, files, severity, confidence, and coverage, not real-world detection rates.
183
-
184
- Five [application-directory snapshots](./test/evaluation/projects.json) provide additional evaluation. Preparation uses the network and verifies Git object hashes; the target must be a new directory outside Git:
213
+ ```powershell
214
+ npm run prepublishOnly
215
+ ```
185
216
 
186
217
  ```powershell
187
- node scripts/fetch-evaluation-projects.mjs "$env:TEMP/canship-evaluation"
218
+ npm run test:package
188
219
  ```
189
220
 
190
- Then evaluate offline without installing or running sample dependencies:
221
+ New rules require positive and negative [fixtures](./test/fixtures/). Run the offline corpus:
191
222
 
192
223
  ```powershell
193
- npm run evaluate:projects -- "$env:TEMP/canship-evaluation"
224
+ npm run evaluate
194
225
  ```
195
226
 
196
- CI uses the same corpus. Git history and deployed behaviour are outside this evaluation.
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.
197
228
 
198
229
  ## License
199
230
 
200
- [MIT](./LICENSE). Supabase and Firebase fixtures retain Apache-2.0; Next.js and `cors` fixtures retain MIT. Each includes its source and licence.
231
+ [MIT](./LICENSE). Supabase/Firebase fixtures retain Apache-2.0; Next.js/`cors` fixtures retain MIT. Sources and licences accompany the fixtures.