canship 0.3.1 → 0.4.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 CHANGED
@@ -2,15 +2,17 @@
2
2
 
3
3
  面向 JavaScript / TypeScript 项目的本地静态扫描器,检测凭据暴露与访问控制配置错误。扫描不执行项目代码、不上传文件、不联网。
4
4
 
5
- 本文档对应 0.3.x,适用于匹配的 [npm 版本](https://www.npmjs.com/package/canship) 或本地构建。
5
+ [English](./README.md)
6
+
7
+ ## 快速开始
6
8
 
7
9
  ```powershell
8
10
  npx canship .
9
11
  ```
10
12
 
11
- 要求 Node.js ≥18,无运行时依赖。`npx` 可能联网下载软件包;扫描仅使用本地文件与 Git 历史。Git 仓库中无法调用 Git 时,扫描标记为未完成。
13
+ 要求 Node.js ≥18,无运行时依赖。安装软件包可能联网;Git 检查仅使用本地历史。仓库中无法调用 Git 时,扫描标记为未完成。
12
14
 
13
- [English](./README.md)
15
+ > 本文对应 0.4.0,较早的 [npm 版本](https://www.npmjs.com/package/canship) 可能不包含下述全部功能。
14
16
 
15
17
  ## 检测范围
16
18
 
@@ -18,64 +20,108 @@ npx canship .
18
20
  |---|---|
19
21
  | 硬编码凭据、私钥及含密码的数据库连接串 | P0 |
20
22
  | 公开环境变量中的私密值 | P0 |
21
- | Supabase 管理员密钥暴露至客户端 | P0 |
23
+ | 源码或公开环境变量中的 Supabase 管理员凭据 | P0 |
22
24
  | Git 跟踪及历史 `.env` 文件中的凭据或疑似私密值 | P0 |
23
- | Supabase 迁移记录中未启用行级安全(RLS)的表 | P1 |
24
- | Firebase 无条件访问及固定日期测试规则 | P1 |
25
- | Next.js API 数据操作未识别到鉴权 | P0 / P1 |
25
+ | Supabase 迁移中未启用行级安全(RLS)的表 | P1 |
26
+ | Supabase 条件恒为真的 RLS 策略 | P1 |
27
+ | 内容可被列举的 Supabase 公开存储桶 | P2 |
28
+ | Firebase 无条件访问及固定日期测试规则(Firestore、Storage、Realtime Database) | P1 |
29
+ | 服务端数据操作未识别到鉴权 | P0 / P1 |
26
30
  | 携带凭据的 CORS 来源回显或通配符配置 | P1 / P2 |
27
31
 
28
- 支持 OpenAI、Anthropic、AWS、Stripe、GitHub、npm、Slack、SendGrid 等凭据格式及常见前端公开环境变量前缀。API 鉴权检查仅覆盖 Next.js `/api`,支持 App Router、Pages Router、路由组和工作区应用。
32
+ 识别 OpenAI、Anthropic、AWS、Stripe、GitHub、npm 等凭据格式及常见前端公开环境变量前缀。规则 ID、范围与限制见 `--list-rules`。
33
+
34
+ ### 鉴权检查范围
35
+
36
+ | 框架 | 检查入口 |
37
+ |---|---|
38
+ | Next.js | `app/` 下的 route 处理函数、Pages Router `/api`、`'use server'` 函数 |
39
+ | SvelteKit | `+server` 端点及 `+page.server` 表单 action |
40
+ | Nuxt | `server/api`、`server/routes` |
41
+ | Remix / React Router | `app/routes` 中导出的 `loader`、`action` |
42
+ | Astro | `src/pages` 中的端点 |
43
+
44
+ 支持路由组和工作区应用,不检查 SvelteKit 页面 load 与 remote function。已识别的 Next.js、Astro 中间件鉴权可抑制覆盖路由的结果;Server Function 需在函数内鉴权。SvelteKit hooks、Nuxt 中间件及本地鉴权函数可降低置信度,但不抑制结果。
45
+
46
+ Supabase 检查重放本地迁移并读取支持的存储桶配置,不检查仅在控制台修改的配置或省略子句隐含的策略条件。
29
47
 
30
- 置信度分为确定(`certain`)和疑似(`likely`),仅描述静态证据,不验证凭据有效性或线上状态。默认只展示确定结果;隐藏的疑似结果仍影响退出码。
48
+ ### 置信度与证据
31
49
 
32
- ## 用法
50
+ `certain`(确定)与 `likely`(疑似)描述静态证据,不验证凭据有效性或线上状态。默认仅展示 `certain`;隐藏的 `likely` 仍影响退出码。
33
51
 
34
- 省略路径时扫描当前目录。
52
+ 管理员客户端相关结果附带数据操作、导入和客户端构造位置。支持限定语法内的 Supabase 构造器别名、本地鉴权导入、重导出及返回函数的封装。鉴权解析最多 8 跳,证据链最多 24 步,截断时提示。间接鉴权证据仅降低置信度;导入关系不证明运行时数据流。
35
53
 
36
- | 参数 | 说明 |
54
+ ## 命令行
55
+
56
+ 省略路径时扫描当前目录。报告正文为英文。
57
+
58
+ | 参数 | 作用 |
37
59
  |---|---|
38
- | `-a`, `--all` | 展示疑似结果 |
60
+ | `-a`, `--all` | 所有格式包含 `likely` 结果 |
39
61
  | `--json` | 输出 JSON |
40
62
  | `--fix-prompt` | 输出修复指令及独立的人工操作清单 |
41
63
  | `--report[=file]` | 写入 HTML,默认 `canship-report.html` |
42
64
  | `--sarif[=file]` | 写入 SARIF 2.1.0,默认 `canship.sarif` |
43
- | `--best-effort` | 无结果时,允许不完整扫描退出 `0` |
44
- | `--baseline[=file]` | 应用基线,默认 `canship-baseline.json` |
45
- | `--baseline-write[=file]` | 写入当前结果为基线后退出,同上默认路径 |
46
- | `--only=ids` | 仅执行匹配规则,支持逗号分隔及重复参数 |
47
- | `--skip=ids` | 排除匹配规则,支持逗号分隔及重复参数 |
65
+ | `--no-excerpts` | 省略源码摘录,不改变结果和退出码 |
66
+ | `--changed-since=ref` | 按变更文件筛选报告,不改变扫描范围和退出码 |
67
+ | `--only=ids` / `--skip=ids` | 选择或排除规则,支持逗号分隔及重复参数 |
68
+ | `--list-rules` | 列出规则,不扫描;支持 `--json` |
69
+ | `--baseline[=file]` | 抑制基线结果,默认 `canship-baseline.json` |
70
+ | `--baseline-write[=file]` | 记录结果后退出,默认路径同上 |
48
71
  | `--no-config` | 忽略项目配置 |
49
- | `--no-ignore-markers` | 不遵从被扫描源码中的忽略标记 |
50
- | `--list-rules` | 列出规则及限制,不扫描;支持 `--json` |
51
- | `--no-excerpts` | 所有报告省略源码摘录,不改变结果和退出码 |
52
- | `-h`, `--help` | 显示帮助 |
53
- | `-v`, `--version` | 显示版本 |
72
+ | `--no-ignore-markers` | 不遵从源码忽略标记 |
73
+ | `--best-effort` | 无结果时,允许不完整扫描退出 `0` |
74
+ | `-h`, `--help` / `-v`, `--version` | 显示帮助或版本 |
54
75
 
55
- `--json` 与 `--fix-prompt` 互斥;HTML、SARIF 可与任一模式组合。报告正文为英文,`--all` 对所有格式生效。
76
+ `--json` 与 `--fix-prompt` 互斥;HTML、SARIF 可与任一模式组合。
56
77
 
57
78
  ### 退出码
58
79
 
59
80
  | 退出码 | 含义 |
60
81
  |---|---|
61
- | `0` | 无结果,且扫描完整或由 `--best-effort` 接受不完整扫描 |
82
+ | `0` | 无结果;扫描完整,或由 `--best-effort` 接受不完整状态 |
62
83
  | `1` | 存在 `certain` 的 P0/P1 结果 |
63
- | `2` | 存在其他结果,包括被隐藏的 `likely` |
84
+ | `2` | 存在其他结果,包括隐藏的 `likely` |
64
85
  | `3` | 参数或工具错误,或未被接受的不完整扫描 |
65
86
 
66
- 结果退出码优先于不完整状态;`--best-effort` 不改变 `1` 或 `2`。
87
+ 统计以规则筛选、忽略标记和基线处理后的结果为准。结果退出码优先于不完整状态;`--best-effort` 不改变 `1` 或 `2`。
67
88
 
68
- ### 机器可读输出
89
+ ### 变更文件视图
69
90
 
70
- JSON 使用独立于包版本的 `schemaVersion: 1`,npm 包附带 [结构定义](./schemas/scan-report-v1.schema.json)。调用方应兼容新增字段、拒绝不支持的结构版本;`--list-rules --json` 为独立的 `kind: "rule-catalog"` 文档。
91
+ `--changed-since=origin/main` 比较本地共同祖先与工作区,包含未被 Git 忽略的新文件,不拉取远程。仍扫描全项目,仅展示主位置或证据位置发生变更的结果;仓库级结果及证据链截断的结果保留。
71
92
 
72
- `findings` 为抑制和筛选后的结果;`hiddenLikely`、`baselineSuppressed`、`baselineStale` 提供相关统计。完整性需另查 `partial`、`errors`、`skipped`、`filesScanned`;SARIF 提供执行状态与诊断通知。
93
+ 隐藏结果仍影响退出码:此功能用于审阅,不是“仅新增问题阻断 CI”的策略。缺少 Git、引用或共同历史时退出 `3`,`--best-effort` 不豁免。不能与 `--baseline-write` 组合。
73
94
 
74
- ## GitHub Action
95
+ ### 结构化报告
96
+
97
+ JSON 使用 `schemaVersion: 1`,包内附带 [结构定义](./schemas/scan-report-v1.schema.json)。调用方应兼容新增字段,拒绝不支持的结构版本。
98
+
99
+ - `findings`:抑制及展示筛选后的结果。
100
+ - `hiddenLikely`、`baselineSuppressed`、`baselineStale`:筛选与基线统计。
101
+ - `partial`、`errors`、`skipped`、`filesScanned`:扫描完整性,须独立于退出码检查。
102
+ - `changeView`:启用变更视图时的筛选统计及全量扫描统计。
75
103
 
76
- 保存为 `.github/workflows/canship.yml`,在推送和 PR 时扫描并生成统计摘要。安装扫描器需要联网,扫描不联网;不安装或执行项目依赖,默认不上传 SARIF。
104
+ SARIF 包含执行诊断与证据关联位置。`--list-rules --json` 返回独立的 `kind: "rule-catalog"` 文档。
77
105
 
78
- 示例固定 Action 提交,显式安装 npm 版 `0.3.0`;`version` 不使用仓库中的未发布源码。Action 兼容 0.2.1 无 `schemaVersion` 的报告。
106
+ ## 程序化 API
107
+
108
+ 提供 Node.js ESM 入口及 TypeScript 类型:
109
+
110
+ ```js
111
+ import { scan, summarize, listRules } from 'canship'
112
+
113
+ const result = await scan('./my-app', { noExcerpts: true })
114
+ console.log(summarize(result))
115
+ console.log(listRules())
116
+ ```
117
+
118
+ `scan()` 返回全部置信度结果,支持 `only`、`skip`、`honorIgnoreMarkers`(默认 `true`)、`noExcerpts`(默认 `false`)。不读取项目配置、不应用基线、不写报告、不设置进程退出码。无效参数或根目录抛出异常;扫描缺口保留在结果中。
119
+
120
+ `summarize()` 返回结果数、阻断数、疑似数、`partial` 及默认 CLI 退出码。`listRules()` 返回独立的规则目录副本。
121
+
122
+ ## GitHub Action
123
+
124
+ 保存为 `.github/workflows/canship.yml`。Action 安装指定 npm 版本,扫描检出目录并生成统计摘要;不安装或执行项目依赖,SARIF 需显式启用上传。
79
125
 
80
126
  ```yaml
81
127
  name: canship
@@ -90,25 +136,27 @@ jobs:
90
136
  with:
91
137
  fetch-depth: 0
92
138
  persist-credentials: false
93
- - uses: Tasomei/canship@8b1a3aa88c77e92e2806b343af6003372855bc70
139
+ - uses: Tasomei/canship@b4cbbfe6b5c4c88164b9388d121f7651032259a4
94
140
  with:
95
- version: '0.3.0'
141
+ version: '0.4.0'
96
142
  ```
97
143
 
144
+ 提交号固定 Action 实现;`version` 选择 npm 扫描器,不使用仓库源码。该固定提交默认安装 0.3.2;示例显式选择 0.4.0。
145
+
98
146
  | 输入 | 默认值 | 说明 |
99
147
  |---|---|---|
100
148
  | `path` | `.` | 检出目录内的扫描路径 |
101
- | `version` | `0.3.0` | 精确 npm 版本,不接受范围或标签 |
102
- | `fail-on` | `blocking` | `blocking`:确定的 P0/P1;`any`:全部结果;`none`:仅报告结果 |
103
- | `only` / `skip` | 未设置 | 互斥,逗号分隔的规则选择器 |
149
+ | `version` | `0.3.2` | 精确 npm 版本,不接受范围或标签 |
150
+ | `fail-on` | `blocking` | `blocking`:确定的 P0/P1;`any`:全部结果;`none`:仅报告 |
151
+ | `only` / `skip` | 未设置 | 互斥的规则选择器 |
104
152
  | `baseline` | 未设置 | 相对扫描目录的已有基线 |
105
153
  | `use-config` | `false` | 启用项目配置 |
106
- | `upload-sarif` | `false` | 上传 SARIF 至 GitHub 代码扫描 |
107
- | `category` | `canship` | 每个扫描目标使用独立分类 |
154
+ | `upload-sarif` | `false` | 上传至 GitHub 代码扫描 |
155
+ | `category` | `canship` | 扫描目标的 SARIF 分类 |
108
156
 
109
- 输出:`exit-code`、`findings`、`blocking`、`partial`。策略统计包含疑似结果,基线与忽略仍生效;扫描不完整、工具错误或报告不兼容始终失败,包括 `fail-on: none`。
157
+ 输出:`exit-code`、`findings`、`blocking`、`partial`。统计包含基线与排除处理后的疑似结果。扫描不完整、工具错误或报告不兼容始终失败,`fail-on: none` 也不例外。
110
158
 
111
- 上传 SARIF 需 `security-events: write` 及 [GitHub 代码扫描支持](https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file),Fork PR 可能无权限。上传前审阅报告中的路径与详情;不可信 PR 使用 `pull_request`,不要使用 `pull_request_target`。Action 为后续步骤设置 Node.js 22,需要其他版本时应使用独立扫描任务。
159
+ 上传 SARIF 需 `security-events: write` 及 [代码扫描支持](https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file),Fork PR 可能权限不足。上传前需审阅报告。不可信 PR 使用 `pull_request`,不要使用 `pull_request_target`。Action 为后续步骤设置 Node.js 22;需要其他版本时使用独立扫描任务。
112
160
 
113
161
  ## 配置与基线
114
162
 
@@ -121,18 +169,18 @@ jobs:
121
169
  }
122
170
  ```
123
171
 
124
- 命令行参数优先;`only`、`skip` 互斥,接受规则 ID 或命名空间。未选规则不执行,`ruleSelection.removed` 仅统计已执行规则中被过滤的结果。扫描不可信项目时同时使用 `--no-config --no-ignore-markers`,二者均由被扫描项目控制;`bestEffort` 仅限命令行设置。
172
+ 命令行参数优先。`only`、`skip` 互斥,接受规则 ID 或命名空间。`--best-effort` 仅限命令行。扫描不可信项目时使用 `--no-config --no-ignore-markers`。
125
173
 
126
174
  ### 忽略标记
127
175
 
128
- 独占注释行的 `canship-ignore-file` 排除整个文件;`canship-ignore-next-line` 忽略下一行,可附规则 ID:
176
+ 独占注释行的 `canship-ignore-file` 排除整个文件;`canship-ignore-next-line` 抑制下一行,可限定规则:
129
177
 
130
178
  ```ts
131
179
  // canship-ignore-next-line cors/wildcard-with-credentials
132
180
  const corsOptions = { origin: '*', credentials: true }
133
181
  ```
134
182
 
135
- 报告披露忽略、规则筛选和基线抑制信息;主动排除不标记为未完成。标记可使退出码降为 `0`;`--no-config` 不影响标记,`--no-ignore-markers` 使两种标记均失效。
183
+ 报告披露排除与抑制信息。主动排除不标记为未完成,可使退出码降为 `0`。`--no-config` 不禁用标记,`--no-ignore-markers` 才会禁用。
136
184
 
137
185
  ### 基线
138
186
 
@@ -142,29 +190,27 @@ const corsOptions = { origin: '*', credentials: true }
142
190
  npx canship --baseline-write
143
191
  ```
144
192
 
145
- 仅报告新增结果:
193
+ 后续扫描抑制这些结果:
146
194
 
147
195
  ```powershell
148
196
  npx canship --baseline
149
197
  ```
150
198
 
151
- 默认基线位于扫描目录,显式路径相对工作目录。写入成功退出 `0`,不代表无问题;扫描不完整或启用规则筛选时会提示。
199
+ 默认路径相对扫描目录,显式路径相对工作目录;读取与写入模式互斥。写入成功退出 `0`,不表示无问题;扫描不完整或启用规则筛选时会提示。
152
200
 
153
- 基线格式为 v2,移动行号不改变指纹,替换凭据会改变。缺失、损坏及 v1 基线均退出 `3`。基线不含源码,但包含路径、规则和问题描述,提交前需审阅。
201
+ 基线格式为 v2,移动行号不改变指纹,替换凭据会改变。缺失、损坏及 v1 基线均退出 `3`。基线不含源码摘录,但包含路径、规则和问题描述,提交前需审阅。
154
202
 
155
203
  ## 隐私与限制
156
204
 
157
- - 静态分析可能误报或漏报,不验证线上行为,不覆盖限流、注入、依赖漏洞或业务授权。无结果不等于安全。
158
- - 脱敏仅覆盖已识别格式,未知秘密可能出现在源码摘录中。`--no-excerpts` 省略摘录,JSON 以 `excerptsOmitted` 标明;路径、名称、说明及基线不匿名化,分享前仍需审阅。
159
- - Google/Firebase/Maps 的 `AIza...` 值按公开标识符处理,不单凭其值判定泄露。
205
+ - 静态检查可能漏报或将预期配置报为问题,不验证线上行为,不覆盖限流、注入、依赖漏洞或业务授权。无结果不等于安全。
206
+ - 脱敏仅覆盖已识别格式。未识别的敏感值可能保留在摘录中;`--no-excerpts` 移除摘录,并设置 JSON `excerptsOmitted`。路径、名称、说明和基线不匿名化。
207
+ - Google/Firebase/Maps 的 `AIza...` 密钥按公开标识符处理,不单凭其值判定泄露。
160
208
  - 读取上限:单文件 2 MiB,单次 128 MiB、10,000 个文件,目录 16 层。每文件跨规则最多 100 条结果,优先保留高严重度、高置信度结果。
161
- - Git 历史每文件最多 100 个相关版本,单条 Git 命令超时 30 秒。超限、超时均报告检查缺口。
162
- - 不跟随符号链接;嵌套仓库、子模块需单独扫描。范围内跳过项使扫描未完成,内置排除的构建和依赖目录除外。
209
+ - Git 历史每文件最多 100 个相关版本,单条命令超时 30 秒;Supabase 策略及存储桶语句最多解析 4,000 个字符。超限报告扫描未完成。
210
+ - 不跟随符号链接;嵌套仓库、子模块需单独扫描。范围内跳过项使扫描未完成,内置排除的依赖和构建目录除外。
163
211
 
164
212
  ## 开发
165
213
 
166
- 新增规则需包含应检出与不应检出的 [测试用例](./test/fixtures/)。
167
-
168
214
  ```powershell
169
215
  npm ci
170
216
  ```
@@ -173,28 +219,30 @@ npm ci
173
219
  npm run prepublishOnly
174
220
  ```
175
221
 
176
- 离线评估:
222
+ ```powershell
223
+ npm run test:package
224
+ ```
225
+
226
+ 新增规则需包含应检出与不应检出的 [夹具](./test/fixtures/)。运行离线 [评估集](./test/fixtures/evaluation/):
177
227
 
178
228
  ```powershell
179
229
  npm run evaluate
180
230
  ```
181
231
 
182
- 评估集含 10 个构造用例、9 个固定版本上游示例及变体,同时纳入 `npm test`;[来源与许可](./test/fixtures/evaluation/) 随样本保存。断言覆盖规则、文件、严重度、置信度与扫描完整性,不代表真实项目检出率。
183
-
184
- 另有 5 个 [应用目录快照](./test/evaluation/projects.json)。准备阶段联网并校验 Git 对象摘要,目标须为 Git 仓库外的新目录:
232
+ [应用快照](./test/evaluation/projects.json) 需先联网获取并校验来源,目标为 Git 仓库外的新目录:
185
233
 
186
234
  ```powershell
187
235
  node scripts/fetch-evaluation-projects.mjs "$env:TEMP/canship-evaluation"
188
236
  ```
189
237
 
190
- 随后离线评估,不安装或运行样本依赖:
238
+ 随后离线评估:
191
239
 
192
240
  ```powershell
193
241
  npm run evaluate:projects -- "$env:TEMP/canship-evaluation"
194
242
  ```
195
243
 
196
- CI 使用同一评估集,不验证 Git 历史或线上行为。
244
+ 项目评估使用临时副本,比较原项目及开放、受限测试变体的全部结果,不安装或运行样本依赖。这些测试不衡量真实项目检出率、Git 历史覆盖率或线上行为。
197
245
 
198
246
  ## 许可
199
247
 
200
- [MIT](./LICENSE)。Supabase、Firebase 样本保留 Apache-2.0,Next.js、`cors` 样本保留 MIT;各样本附来源及许可证。
248
+ [MIT](./LICENSE)。Supabase、Firebase 夹具保留 Apache-2.0,Next.js、`cors` 夹具保留 MIT;来源与许可证随夹具保存。
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. Package installation may use the network. Git checks use local history only; unavailable Git in a repository marks the scan incomplete.
12
14
 
13
- [简体中文](./README-zh-CN.md)
15
+ > Documentation for 0.4.0. Earlier [npm versions](https://www.npmjs.com/package/canship) may not include all features below.
14
16
 
15
17
  ## Checks
16
18
 
@@ -18,64 +20,108 @@ 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 |
23
+ | Supabase admin credentials in source or public environment variables | P0 |
22
24
  | Credentials or suspected private values in Git-tracked and historical `.env` files | P0 |
23
25
  | Supabase tables without Row Level Security (RLS) in migrations | P1 |
24
- | Firebase unconditional access and date-based test rules | P1 |
25
- | Next.js API data operations without recognised authentication | P0 / P1 |
26
+ | Supabase RLS policies with always-true conditions | P1 |
27
+ | Public Supabase storage buckets with listable contents | P2 |
28
+ | Firebase unconditional access and date-based test rules (Firestore, Storage, Realtime Database) | P1 |
29
+ | Server-side data operations without recognised authentication | P0 / P1 |
26
30
  | Credentialed CORS with reflected or wildcard origins | P1 / P2 |
27
31
 
28
- Supports OpenAI, Anthropic, AWS, Stripe, GitHub, npm, Slack, SendGrid, and other credential formats, plus common frontend public environment prefixes. API authentication checks cover only Next.js `/api`: App Router, Pages Router, route groups, and workspace applications.
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 |
37
+ |---|---|
38
+ | Next.js | Route handlers under `app/`, Pages Router `/api`, and `'use server'` functions |
39
+ | SvelteKit | `+server` endpoints and `+page.server` form actions |
40
+ | Nuxt | `server/api` and `server/routes` |
41
+ | Remix / React Router | `loader` and `action` exports in `app/routes` |
42
+ | Astro | Endpoints in `src/pages` |
43
+
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.
29
45
 
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.
46
+ Supabase checks replay local migrations and read supported bucket configuration. Dashboard-only changes and policy conditions implied by missing clauses are not checked.
31
47
 
32
- ## Usage
48
+ ### Confidence and evidence
33
49
 
34
- Omitting the path scans the current directory.
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.
35
51
 
36
- | Option | Description |
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.
53
+
54
+ ## CLI
55
+
56
+ Omitting the path scans the current directory. Reports are in English.
57
+
58
+ | Option | Effect |
37
59
  |---|---|
38
- | `-a`, `--all` | Include likely findings |
60
+ | `-a`, `--all` | Include `likely` findings in every format |
39
61
  | `--json` | Output JSON |
40
- | `--fix-prompt` | Output remediation instructions and separate manual actions |
62
+ | `--fix-prompt` | Output repair instructions and separate manual actions |
41
63
  | `--report[=file]` | Write HTML; default: `canship-report.html` |
42
64
  | `--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 |
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 |
48
71
  | `--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 |
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 |
54
75
 
55
- `--json` and `--fix-prompt` are mutually exclusive; HTML and SARIF work with either. Reports are in English. `--all` applies to every format.
76
+ `--json` and `--fix-prompt` are mutually exclusive; HTML and SARIF can accompany either.
56
77
 
57
78
  ### Exit codes
58
79
 
59
80
  | Code | Meaning |
60
81
  |---|---|
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 |
82
+ | `0` | No findings; scan complete or incompleteness accepted by `--best-effort` |
83
+ | `1` | At least one `certain` P0/P1 finding |
84
+ | `2` | Other findings, including hidden `likely` findings |
85
+ | `3` | Invalid arguments, tool error, or unaccepted incomplete scan |
65
86
 
66
- Finding exit codes take precedence over incompleteness; `--best-effort` does not change `1` or `2`.
87
+ Counts apply after rule selection, ignore markers, and baselines. Findings take precedence over incompleteness; `--best-effort` does not change `1` or `2`.
67
88
 
68
- ### Machine-readable output
89
+ ### Changed-file view
69
90
 
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.
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.
71
92
 
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.
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`.
73
94
 
74
- ## GitHub Action
95
+ ### Structured reports
96
+
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.
98
+
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.
103
+
104
+ SARIF includes execution diagnostics and related evidence locations. `--list-rules --json` returns a separate `kind: "rule-catalog"` document.
75
105
 
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.
106
+ ## Programmatic API
77
107
 
78
- The example pins the Action commit and explicitly installs npm version `0.3.0`; `version` does not use unreleased repository source. The Action accepts 0.2.1 reports without `schemaVersion`.
108
+ Node.js ESM with TypeScript declarations:
109
+
110
+ ```js
111
+ import { scan, summarize, listRules } from 'canship'
112
+
113
+ const result = await scan('./my-app', { noExcerpts: true })
114
+ console.log(summarize(result))
115
+ console.log(listRules())
116
+ ```
117
+
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.
119
+
120
+ `summarize()` returns finding, blocking and likely counts, `partial`, and the default CLI exit code. `listRules()` returns an independent catalog copy.
121
+
122
+ ## GitHub Action
123
+
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.
79
125
 
80
126
  ```yaml
81
127
  name: canship
@@ -90,29 +136,31 @@ jobs:
90
136
  with:
91
137
  fetch-depth: 0
92
138
  persist-credentials: false
93
- - uses: Tasomei/canship@8b1a3aa88c77e92e2806b343af6003372855bc70
139
+ - uses: Tasomei/canship@b4cbbfe6b5c4c88164b9388d121f7651032259a4
94
140
  with:
95
- version: '0.3.0'
141
+ version: '0.4.0'
96
142
  ```
97
143
 
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.
145
+
98
146
  | Input | Default | Meaning |
99
147
  |---|---|---|
100
- | `path` | `.` | Directory within the checkout |
101
- | `version` | `0.3.0` | 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 |
148
+ | `path` | `.` | Scan directory within the checkout |
149
+ | `version` | `0.3.2` | Exact npm version; no ranges or tags |
150
+ | `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 |
105
153
  | `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 |
154
+ | `upload-sarif` | `false` | Upload to GitHub code scanning |
155
+ | `category` | `canship` | SARIF category for the scan target |
108
156
 
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`.
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`.
110
158
 
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.
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.
112
160
 
113
161
  ## Configuration and baselines
114
162
 
115
- Place `canship.config.json` in the scanned directory. Supported keys: `baseline`, `only`, `skip`, `all`.
163
+ `canship.config.json` in the scan directory accepts `baseline`, `only`, `skip`, and `all`:
116
164
 
117
165
  ```json
118
166
  {
@@ -121,18 +169,18 @@ Place `canship.config.json` in the scanned directory. Supported keys: `baseline`
121
169
  }
122
170
  ```
123
171
 
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.
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`.
125
173
 
126
174
  ### Ignore markers
127
175
 
128
- A standalone `canship-ignore-file` comment excludes a file; `canship-ignore-next-line` suppresses the next line, with an optional rule ID:
176
+ A standalone `canship-ignore-file` comment excludes the file. `canship-ignore-next-line` suppresses the next line, optionally for one rule:
129
177
 
130
178
  ```ts
131
179
  // canship-ignore-next-line cors/wildcard-with-credentials
132
180
  const corsOptions = { origin: '*', credentials: true }
133
181
  ```
134
182
 
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.
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.
136
184
 
137
185
  ### Baselines
138
186
 
@@ -142,29 +190,27 @@ Record existing findings:
142
190
  npx canship --baseline-write
143
191
  ```
144
192
 
145
- Report only new findings:
193
+ Suppress them on subsequent scans:
146
194
 
147
195
  ```powershell
148
196
  npx canship --baseline
149
197
  ```
150
198
 
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.
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.
152
200
 
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.
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.
154
202
 
155
- ## Privacy and limitations
203
+ ## Privacy and limits
156
204
 
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.
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.
160
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.
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.
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.
163
211
 
164
212
  ## Development
165
213
 
166
- New rules require positive and negative [test cases](./test/fixtures/).
167
-
168
214
  ```powershell
169
215
  npm ci
170
216
  ```
@@ -173,28 +219,30 @@ npm ci
173
219
  npm run prepublishOnly
174
220
  ```
175
221
 
176
- Offline evaluation:
222
+ ```powershell
223
+ npm run test:package
224
+ ```
225
+
226
+ New rules need positive and negative [fixtures](./test/fixtures/). Run the offline [evaluation corpus](./test/fixtures/evaluation/):
177
227
 
178
228
  ```powershell
179
229
  npm run evaluate
180
230
  ```
181
231
 
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:
232
+ For [application snapshots](./test/evaluation/projects.json), fetch and verify sources into a new directory outside Git:
185
233
 
186
234
  ```powershell
187
235
  node scripts/fetch-evaluation-projects.mjs "$env:TEMP/canship-evaluation"
188
236
  ```
189
237
 
190
- Then evaluate offline without installing or running sample dependencies:
238
+ Then evaluate offline:
191
239
 
192
240
  ```powershell
193
241
  npm run evaluate:projects -- "$env:TEMP/canship-evaluation"
194
242
  ```
195
243
 
196
- CI uses the same corpus. Git history and deployed behaviour are outside this evaluation.
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.
197
245
 
198
246
  ## License
199
247
 
200
- [MIT](./LICENSE). Supabase and Firebase fixtures retain Apache-2.0; Next.js and `cors` fixtures retain MIT. Each includes its source and licence.
248
+ [MIT](./LICENSE). Supabase and Firebase fixtures retain Apache-2.0; Next.js and `cors` fixtures retain MIT. Sources and licences accompany the fixtures.