canship 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/README-zh-CN.md +89 -126
  2. package/README.md +89 -126
  3. package/dist/cli.js +2505 -612
  4. package/dist/index.js +1661 -143
  5. package/package.json +1 -1
package/README-zh-CN.md CHANGED
@@ -1,153 +1,109 @@
1
1
  # canship
2
2
 
3
- 面向 JavaScript / TypeScript 项目的本地静态扫描器,检测凭据暴露与访问控制配置错误。扫描不执行项目代码、不上传文件、不联网。
3
+ 面向 JavaScript / TypeScript Web 应用的本地静态扫描器,检查凭据暴露、访问控制配置及请求输入风险。扫描不执行项目代码、不上传文件、不联网。
4
4
 
5
5
  [English](./README.md)
6
6
 
7
+ > 本文对应 `0.6.0`。使用 `npx canship --version` 确认已安装版本。
8
+
7
9
  ## 快速开始
8
10
 
9
11
  ```powershell
10
- npx canship .
12
+ npx canship
11
13
  ```
12
14
 
13
- 要求 Node.js ≥18,无运行时依赖。安装可能联网;Git 检查仅读取本地历史,仓库中无法调用 Git 时标记扫描未完成。
15
+ 默认扫描当前目录,也可指定路径。要求 Node.js ≥18,无运行时依赖,安装可能联网。Git 检查覆盖本地跟踪文件及提交历史,不访问远程仓库;历史无法读取时标记扫描不完整。
16
+
17
+ 以下报告来自发布前开发构建,使用示例数据。
14
18
 
15
- > 本文对应 0.5.0。`npx canship` 运行 npm 默认版本;其他版本的文档请查阅对应 Git 标签。
19
+ ![终端报告](https://raw.githubusercontent.com/Tasomei/canship/main/docs/images/terminal.png)
16
20
 
17
21
  ## 检测范围
18
22
 
19
- | 检查项 | 级别 |
20
- |---|---|
21
- | 硬编码凭据、私钥及含密码的数据库连接串 | P0 |
22
- | 公开环境变量中的私密值 | P0 |
23
- | 源码或公开环境变量中的 Supabase 管理员凭据 | P0 |
24
- | Git 跟踪或历史提交的 `.env` 文件,模板除外 | P0 |
25
- | Supabase 未启用 RLS 的表及条件恒真的策略 | P1 |
26
- | 内容可被列举的 Supabase 公开存储桶 | P2 |
27
- | Firebase 无条件访问及固定日期测试规则 | P1 |
28
- | 服务端数据操作未识别到鉴权 | P0 / P1 |
29
- | 携带凭据的 CORS 来源回显或通配符配置 | P1 / P2 |
23
+ | 类别 | 级别 | 范围 |
24
+ |---|:---:|---|
25
+ | 凭据 | `P0` | 硬编码密钥、私钥、含密码的数据库连接串、公开变量中的私密值、Supabase 管理员密钥、Git 跟踪或历史中的非模板 `.env` 文件 |
26
+ | API 访问 | `P0/P1` | 未识别到鉴权的数据库操作、服务端信任 Supabase `getSession()`、未验证的 Stripe webhook |
27
+ | 数据库规则 | `P1/P2` | Supabase 表未启用 RLS、无条件放行策略、允许公开列出对象的存储桶;Firebase 开放规则及限时测试规则 |
28
+ | CORS | `P1/P2` | 携带凭据的来源回显或通配符配置 |
29
+ | 请求输入 | `P1/P2` | 请求输入参与 SQL 或命令构造、调用方可控的请求主机和重定向目标 |
30
30
 
31
- 识别 OpenAI、Anthropic、AWS、Stripe、GitHub、npm 等凭据格式。Firebase 检查覆盖 Firestore、Storage、Realtime Database。规则 ID 与范围见 `--list-rules`。
31
+ 凭据格式包括 OpenAI、Anthropic、AWS、Stripe、GitHub、npm 等。Firebase 覆盖 Firestore、Storage、Realtime Database。`--list-rules` 列出规则 ID、范围及局限。
32
32
 
33
- ### 鉴权检查
33
+ ### 服务端入口
34
34
 
35
- | 框架 | 检查入口 |
35
+ | 框架 | 入口 |
36
36
  |---|---|
37
- | Next.js | `app/` 路由处理函数、Pages Router `/api`、`'use server'` 函数 |
38
- | SvelteKit | `+server` 端点及 `+page.server` 表单 action |
37
+ | Next.js | App Router 处理函数、Pages Router `/api`、`'use server'` 函数 |
38
+ | SvelteKit | `+server` 端点、`+page.server` 表单 action |
39
39
  | Nuxt | `server/api`、`server/routes` |
40
40
  | Remix / React Router | `app/routes` 中的 `loader`、`action` 导出 |
41
41
  | Astro | `src/pages` 中的端点 |
42
42
 
43
- 支持路由组、工作区应用、本地辅助函数链、身份别名与解构、实参约束及有界分支/异常分析。原始请求输入、常量、未等待的 Promise 或辅助函数名称本身,不构成本地鉴权依据。
44
-
45
- 已识别的 Next.js/Astro 中间件可抑制覆盖范围内的结果;Server Function 需在函数内检查。本地辅助函数、SvelteKit hooks、Nuxt 中间件可降低置信度,但保留结果。不检查 SvelteKit 页面 load 与 remote function。
43
+ 已识别的 Next.js/Astro 中间件可抑制覆盖范围内的鉴权结果;Server Function 需在函数内检查。本地辅助函数、SvelteKit hooks、Nuxt 中间件可降低置信度,但保留结果。输入分析追踪可见的赋值、解构和字符串构造,不以辅助函数名称证明安全。
46
44
 
47
- `certain`(确定)与 `likely`(疑似)描述静态证据,不验证凭据有效性或运行时安全。默认只展示 `certain`,隐藏的 `likely` 仍影响退出码。管理员客户端结果附带操作、导入、构造及鉴权函数位置。
45
+ 路由分析不覆盖 SvelteKit 页面 load、remote function,以及独立的 Express/Hono/Fastify 处理函数;凭据、CORS 等内容规则仍适用。
48
46
 
49
- ## 命令行
50
-
51
- 省略路径时扫描当前目录。报告正文为英文。
47
+ ## 结果
52
48
 
53
- | 参数 | 作用 |
54
- |---|---|
55
- | `-a`、`--all` | 包含 `likely` 结果 |
56
- | `--json` | 输出 JSON |
57
- | `--fix-prompt` | 输出修复指令及独立的人工操作清单 |
58
- | `--report[=file]` | 写入 HTML,默认 `canship-report.html` |
59
- | `--sarif[=file]` | 写入 SARIF 2.1.0,默认 `canship.sarif` |
60
- | `--no-excerpts` | 省略源码摘录,保留结果和退出码 |
61
- | `--changed-since=ref` | 展示与变更文件相关的结果,退出码仍基于全量扫描 |
62
- | `--only=ids` / `--skip=ids` | 选择或排除规则,支持逗号分隔及重复参数 |
63
- | `--list-rules` | 列出规则而不扫描,支持 `--json` |
64
- | `--baseline[=file]` | 抑制已有结果,默认 `canship-baseline.json` |
65
- | `--baseline-write[=file]` | 记录结果后退出,默认路径同上 |
66
- | `--no-config` | 忽略项目配置 |
67
- | `--no-ignore-markers` | 不遵从源码忽略注释 |
68
- | `--best-effort` | 允许没有结果的不完整扫描退出 `0` |
69
- | `-h`、`--help` / `-v`、`--version` | 显示帮助或版本 |
49
+ 报告正文为英文。终端按文件分组,`--verbose` 展开摘录、说明、证据和修复步骤。HTML 为自包含离线报告,支持筛选、分组、人工操作清单及修复提示复制。
70
50
 
71
- `--json` 与 `--fix-prompt` 互斥;HTML、SARIF 可与任一模式组合。
51
+ ![HTML 报告](https://raw.githubusercontent.com/Tasomei/canship/main/docs/images/report.png)
72
52
 
73
- ### 退出码
53
+ `certain` 表示静态证据充分,`likely` 需人工审阅;测试和示例中的结果降为 `likely`。默认仅展示 `certain`,`--all` 显示全部。置信度仅反映静态证据,不代表凭据有效或风险已在运行时验证。
74
54
 
75
55
  | 退出码 | 含义 |
76
56
  |---|---|
77
- | `0` | 无结果,且扫描完整或已由 `--best-effort` 接受不完整状态 |
57
+ | `0` | 无结果,且扫描完整或由 `--best-effort` 接受 |
78
58
  | `1` | 至少一条 `certain` 的 P0/P1 结果 |
79
59
  | `2` | 其他结果,包括隐藏的 `likely` |
80
60
  | `3` | 参数错误、工具错误或未被接受的不完整扫描 |
81
61
 
82
- 退出码基于规则筛选、忽略标记及基线处理后的结果。结果优先于不完整状态;`--best-effort` 不改变 `1` 或 `2`。
62
+ 退出码基于规则筛选、忽略注释和基线处理后的结果。有结果时优先于扫描不完整;`--best-effort` 不改变 `1` 或 `2`。
83
63
 
84
- ### 变更视图与报告
64
+ ## 命令行
85
65
 
86
- `--changed-since=origin/main` 比较本地共同祖先与工作区,包含未被 Git 忽略的新文件,不拉取远程。仍扫描全项目,仅展示主位置或证据位置发生变更的结果;仓库级结果及证据链截断的结果保留。隐藏结果仍影响退出码。缺少 Git、引用或共同历史时退出 `3`,`--best-effort` 不豁免。不能与 `--baseline-write` 组合。
66
+ `npx canship [path] [options]`
87
67
 
88
- JSON 使用 `schemaVersion: 1`;字段及筛选统计见 [结构定义](./schemas/scan-report-v1.schema.json)。须独立于退出码检查 `partial`、`errors`、`skipped`、`filesScanned`。兼容新增字段,拒绝不支持的结构版本。
68
+ | 参数 | 作用 |
69
+ |---|---|
70
+ | `-a`、`--all` | 包含 `likely` 结果 |
71
+ | `--verbose` | 展开终端结果 |
72
+ | `--report[=file]` | 写入 HTML,默认 `canship-report.html` |
73
+ | `--open` | 打开 `--report` 输出;CI 和非交互终端中禁用 |
74
+ | `--json` | 输出 JSON |
75
+ | `--sarif[=file]` | 写入 SARIF 2.1.0,默认 `canship.sarif` |
76
+ | `--fix-prompt` | 输出修复指令及独立的人工操作清单 |
77
+ | `--no-excerpts` | 移除所有报告中的摘录 |
78
+ | `--changed-since=ref` | 展示变更文件结果,保留全量扫描退出码 |
79
+ | `--only=ids` / `--skip=ids` | 选择或排除规则及命名空间,逗号分隔,可重复 |
80
+ | `--list-rules` | 列出规则而不扫描,支持 `--json` |
81
+ | `--baseline[=file]` / `--baseline-write[=file]` | 抑制或记录结果,默认 `canship-baseline.json` |
82
+ | `--no-config` / `--no-ignore-markers` | 忽略项目配置或源码抑制注释 |
83
+ | `--best-effort` | 允许没有结果的不完整扫描退出 `0` |
84
+ | `-h`、`--help` / `-v`、`--version` | 显示帮助或版本 |
89
85
 
90
- SARIF 包含执行诊断与证据位置。`--list-rules --json` 返回独立的 `kind: "rule-catalog"` 文档。
86
+ `--json` 与 `--fix-prompt` 互斥,均可同时输出 HTML 和 SARIF。
87
+
88
+ `--changed-since` 比较本地共同祖先与工作区,包含未被忽略的新文件,不拉取远程、不缩小扫描范围。缺少 Git、引用或共同历史时退出 `3`;不能与 `--baseline-write` 组合。
91
89
 
92
90
  ## 配置
93
91
 
94
- 扫描目录中的 `canship.config.json` 支持 `baseline`、`only`、`skip`、`all`:
92
+ `canship.config.json` 支持 `baseline`、`only`、`skip`、`all`。命令行参数优先,`only` 与 `skip` 互斥。
95
93
 
96
94
  ```json
97
- {
98
- "skip": ["cors/wildcard-with-credentials"],
99
- "all": false
100
- }
101
- ```
102
-
103
- 命令行参数优先。`only`、`skip` 互斥,接受规则 ID 或命名空间。`--best-effort` 仅限命令行。不可信项目使用 `--no-config --no-ignore-markers`。
104
-
105
- ### 忽略注释
106
-
107
- 独占注释行的 `canship-ignore-file` 排除整个文件;`canship-ignore-next-line` 抑制下一行,可限定规则:
108
-
109
- ```ts
110
- // canship-ignore-next-line cors/wildcard-with-credentials
111
- const corsOptions = { origin: '*', credentials: true }
95
+ { "skip": ["cors/wildcard-with-credentials"], "all": false }
112
96
  ```
113
97
 
114
- 报告披露排除信息。主动抑制不标记扫描未完成,可使退出码降为 `0`。`--no-config` 不禁用这些注释。
115
-
116
- ### 基线
117
-
118
- 记录已有结果,后续扫描再抑制:
119
-
120
- ```powershell
121
- npx canship --baseline-write
122
- ```
123
-
124
- ```powershell
125
- npx canship --baseline
126
- ```
127
-
128
- 默认路径相对扫描目录,显式路径相对工作目录;读取与写入模式互斥。写入成功退出 `0`,不代表扫描无问题;扫描不完整或启用规则筛选时会提示。
129
-
130
- v2 指纹不受行号移动影响,凭据变化会改变指纹。缺失、损坏及 v1 基线均退出 `3`。基线不含摘录,但保留路径、规则和描述,提交前需审阅。
131
-
132
- ## API
133
-
134
- 提供 Node.js ESM 入口与 TypeScript 类型:
98
+ 独占行注释 `canship-ignore-file` 排除整个文件;`canship-ignore-next-line [rule]` 抑制下一行,可限定单条规则。报告披露排除项;主动抑制不标记扫描不完整,可能使退出码降为 `0`。扫描不可信项目时使用 `--no-config --no-ignore-markers`。
135
99
 
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
- ```
143
-
144
- `scan()` 返回全部置信度结果,支持 `only`、`skip`、`honorIgnoreMarkers`(默认 `true`)、`noExcerpts`(默认 `false`)。不加载配置、不应用基线、不写报告、不设置退出码。无效参数或根目录抛出异常;扫描缺口保留在结果中。
100
+ 基线表示接受已有结果,不代表问题已修复。v2 格式不受行号移动影响,但凭据变化会重新报告。
145
101
 
146
- `summarize()` 返回结果统计、`partial` 及默认 CLI 退出码。`listRules()` 返回独立的规则目录副本。
102
+ 默认路径相对扫描目录,显式路径相对工作目录;读取与写入互斥。缺失、无效或 v1 基线退出 `3`。写入成功退出 `0`,不完整或选择性扫描会提示。
147
103
 
148
104
  ## GitHub Action
149
105
 
150
- 保存为 `.github/workflows/canship.yml`。Action 安装指定 npm 扫描器并输出统计摘要,不安装或运行项目依赖;SARIF 需显式启用上传。
106
+ 保存为 `.github/workflows/canship.yml`:
151
107
 
152
108
  ```yaml
153
109
  name: canship
@@ -162,47 +118,56 @@ jobs:
162
118
  with:
163
119
  fetch-depth: 0
164
120
  persist-credentials: false
165
- - uses: Tasomei/canship@dfc17be52684314c8631d665074c133bf1170888
121
+ - uses: Tasomei/canship@97c14d1f1e494a49adf716c455b597edf6ae1d88
166
122
  with:
167
- version: '0.5.0'
123
+ version: '0.6.0'
168
124
  honor-ignore-markers: false
169
125
  ```
170
126
 
171
- 提交号固定 Action 实现;`version` 选择 npm 扫描器,不使用仓库源码。该固定实现默认安装 0.4.0。
127
+ 提交哈希固定 Action 实现,`version` 指定 npm 扫描器版本,不使用开发分支源码。Action 使用 Node.js 22,不安装或运行项目依赖,仅输出统计摘要。
172
128
 
173
- | 输入 | 默认值 | 说明 |
129
+ | 输入 | 默认值 | 含义 |
174
130
  |---|---|---|
175
- | `version` | `0.4.0` | 精确 npm 版本,不接受范围或标签 |
131
+ | `version` | `0.5.0` | 精确 npm 扫描器版本 |
176
132
  | `fail-on` | `blocking` | `blocking`:确定的 P0/P1;`any`:全部结果;`none`:仅报告 |
177
- | `use-config` | `false` | 启用项目配置 |
178
- | `honor-ignore-markers` | `true` | 遵从整文件及逐行忽略注释 |
179
- | `upload-sarif` | `false` | 上传至 GitHub 代码扫描 |
180
133
 
181
- 路径、规则筛选、基线及分类输入见 [action.yml](./action.yml)。
134
+ 扫描不完整或工具错误始终失败。默认不读取项目配置、不上传 SARIF。输入输出见 [action.yml](./action.yml)。
182
135
 
183
- 输出:`exit-code`、`findings`、`blocking`、`partial`。统计包含抑制后的疑似结果。扫描不完整、工具错误或报告不兼容始终失败,`fail-on: none` 也不例外。
136
+ 上传 SARIF 需 `security-events: write` 及代码扫描支持,fork PR 可能权限不足;上传前应审阅报告。不可信 PR 使用 `pull_request`,不要使用 `pull_request_target`。
184
137
 
185
- 上传 SARIF 需 `security-events: write` 及代码扫描支持;Fork PR 可能权限不足。上传前需审阅报告。不可信 PR 使用 `pull_request`,不要使用 `pull_request_target`。Action 设置 Node.js 22,必要时使用独立扫描任务。
138
+ ## API 与结构化输出
139
+
140
+ ```js
141
+ import { scan, summarize } from 'canship'
142
+
143
+ const result = await scan('./my-app', { noExcerpts: true })
144
+ console.log(summarize(result))
145
+ ```
146
+
147
+ `scan()` 返回全部置信度结果,支持 `only`、`skip`、`honorIgnoreMarkers`(默认 `true`)、`noExcerpts`(默认 `false`)。不加载配置、不应用基线、不写报告、不设置进程退出码;无效参数抛出异常。`listRules()` 返回规则目录。
148
+
149
+ JSON 使用 [schemaVersion 1](./schemas/scan-report-v1.schema.json)。须独立于退出码检查 `partial`、`errors`、`skipped`、`filesScanned`。SARIF 包含证据位置和执行诊断。
186
150
 
187
151
  ## 隐私与限制
188
152
 
189
- - 静态检查可能漏报或将预期配置报为问题,不验证线上行为、业务授权、限流、注入或依赖漏洞。无结果不等于安全。
190
- - 脱敏仅覆盖已识别格式。未知敏感值可能保留在摘录中;`--no-excerpts` 移除摘录并设置 JSON `excerptsOmitted`。路径、名称、描述和基线不匿名化。
191
- - Google/Firebase/Maps 的 `AIza...` 密钥按公开标识符处理,不单凭其值判定泄露。
192
- - Supabase 检查依据本地迁移及支持的存储桶配置,不检查控制台专属改动或省略子句隐含的策略条件。
193
- - 不跟随符号链接;嵌套仓库与子模块需单独扫描。范围内跳过项使扫描未完成,内置依赖/构建目录排除除外。
153
+ - 静态检查可能误报或漏报,不验证业务授权、限流、依赖漏洞或线上配置。
154
+ - 脱敏仅覆盖已识别格式,未知敏感值可能保留在摘录中;`--no-excerpts` 可移除摘录。路径、名称和基线描述仍可见。
155
+ - Google/Firebase/Maps 的 `AIza…` 密钥按公开标识符处理,不单凭其值判定泄露。Supabase 检查依据本地迁移及支持的存储桶配置。
156
+ - 评估快照获取和可选的 SARIF 上传可能联网。
157
+ - 不跟随符号链接;嵌套仓库与子模块需单独扫描。范围内跳过项及分析超限标记扫描不完整;默认排除的依赖和构建目录不计为扫描缺口。
194
158
 
195
- | 限制 | 上限 |
159
+ | 项目 | 上限 |
196
160
  |---|---|
197
- | 文件读取,含探测 | 单文件 2 MiB;单次 128 MiB、10,000 个文件 |
198
- | 目录遍历 | 50,000 个条目、16 层 |
199
- | 结果数量 | 每文件 100 条,优先保留高严重度、高置信度结果 |
161
+ | 文件读取 | 单文件 2 MiB;单次 128 MiB、10,000 个文件,含探测 |
162
+ | 目录遍历 | 50,000 个条目;16 层 |
163
+ | 结果 | 每文件 100 条,优先保留高严重度、高置信度结果 |
200
164
  | Git 历史 | 每文件 100 个相关版本;单条命令 30 秒 |
201
165
  | 鉴权解析 | 8 跳;每个路由文件 128 个符号 |
202
- | 身份/控制流 | 值解析 8 步;表达式 4,000 字符;每函数 512 个赋值/条件区域;分支/异常区域嵌套 8 层 |
166
+ | 身份/控制流 | 值解析 8 步;表达式 4,000 字符;每函数 512 个赋值/区域;区域嵌套 8 层 |
167
+ | 请求输入追踪 | 值解析 8 步;512 个赋值/区域;表达式 4,000 字符;URL 分析 8 层、静态前缀 200 字符 |
203
168
  | Supabase 策略/存储桶解析 | 单条语句 4,000 字符 |
204
169
 
205
- 扫描或分析超限会报告未完成。证据链最多 24 步,截断时提示。身份获取函数名及导入关系仍属语法证据,不验证运行时实现。
170
+ 证据链最多 24 步,截断时提示。
206
171
 
207
172
  ## 开发
208
173
 
@@ -218,14 +183,12 @@ npm run prepublishOnly
218
183
  npm run test:package
219
184
  ```
220
185
 
221
- 新增规则须包含应检出与不应检出的 [夹具](./test/fixtures/)。运行离线评估:
222
-
223
186
  ```powershell
224
187
  npm run evaluate
225
188
  ```
226
189
 
227
- 应用评估见 [快照清单](./test/evaluation/projects.json)、[获取脚本](./scripts/fetch-evaluation-projects.mjs) 及 [离线评估器](./scripts/evaluate-projects.ts)。测试在临时副本中比较原项目与成对变体,不运行样本依赖;不衡量真实检出率、Git 历史覆盖率或线上行为。
190
+ 新增规则需包含应检出和不应检出的 [夹具](./test/fixtures/)。固定项目评估见 [清单](./test/evaluation/projects.json)、[获取脚本](./scripts/fetch-evaluation-projects.mjs)、[评估器](./scripts/evaluate-projects.ts)。样本通过不代表真实检出率。
228
191
 
229
192
  ## 许可
230
193
 
231
- [MIT](./LICENSE)。Supabase/Firebase 夹具保留 Apache-2.0,Next.js/`cors` 夹具保留 MIT;来源与许可证随夹具保存。
194
+ [MIT](./LICENSE)。Supabase/Firebase 夹具保留 Apache-2.0,Next.js/`cors` 夹具保留 MIT。
package/README.md CHANGED
@@ -1,153 +1,109 @@
1
1
  # canship
2
2
 
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.
3
+ A local static scanner for JavaScript and TypeScript web apps. Checks exposed credentials, access-control configuration, and unsafe request-input flows. No project-code execution, uploads, or network requests during scanning.
4
4
 
5
5
  [简体中文](./README-zh-CN.md)
6
6
 
7
+ > Documentation for `0.6.0`. Check the installed version with `npx canship --version`.
8
+
7
9
  ## Quick start
8
10
 
9
11
  ```powershell
10
- npx canship .
12
+ npx canship
11
13
  ```
12
14
 
13
- Requires Node.js ≥18; no runtime dependencies. Installation may use the network. Git checks read local history only; unavailable Git in a repository marks coverage incomplete.
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
- > Documentation for 0.5.0. `npx canship` runs the npm default version; use the corresponding Git tag for other releases.
19
+ ![Terminal report](https://raw.githubusercontent.com/Tasomei/canship/main/docs/images/terminal.png)
16
20
 
17
21
  ## Checks
18
22
 
19
- | Check | Severity |
20
- |---|---|
21
- | Hardcoded credentials, private keys, and database URLs containing passwords | P0 |
22
- | Private values in public environment variables | P0 |
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 |
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
- 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.
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
- ### Authentication
33
+ ### Server entry points
34
34
 
35
- | Framework | Checked entry points |
35
+ | Framework | Entry points |
36
36
  |---|---|
37
- | Next.js | `app/` route handlers, Pages Router `/api`, and `'use server'` functions |
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` and `server/routes` |
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
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.
43
+ Recognised Next.js/Astro middleware may suppress covered auth findings; Server Functions need local checks. Local helpers, SvelteKit hooks, and Nuxt middleware may lower confidence without suppressing findings. Input analysis follows visible assignments, destructuring, and string construction; helper names alone do not prove sanitisation.
44
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.
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
45
+ SvelteKit page loads, remote functions, and standalone Express/Hono/Fastify handlers are outside route analysis. Content-based checks, including credentials and CORS, still apply.
50
46
 
51
- Omitting the path scans the current directory. Reports are in English.
47
+ ## Results
52
48
 
53
- | Option | Effect |
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 |
49
+ Reports are in English. The terminal groups findings by file; `--verbose` adds excerpts, explanations, evidence, and fixes. HTML is a self-contained offline report with filters, grouping, manual steps, and copyable fix prompts.
70
50
 
71
- `--json` and `--fix-prompt` are mutually exclusive. HTML and SARIF can accompany either.
51
+ ![HTML report](https://raw.githubusercontent.com/Tasomei/canship/main/docs/images/report.png)
72
52
 
73
- ### Exit codes
53
+ `certain` indicates strong static evidence; `likely` requires review. Findings in tests and examples are downgraded to `likely`. Default output shows only `certain`; `--all` includes both. Confidence reflects static evidence, not credential validity or runtime verification.
74
54
 
75
- | Code | Meaning |
55
+ | Exit | Meaning |
76
56
  |---|---|
77
- | `0` | No findings; coverage complete or accepted by `--best-effort` |
57
+ | `0` | No findings; coverage complete or accepted with `--best-effort` |
78
58
  | `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 |
59
+ | `2` | Other findings, including hidden `likely` results |
60
+ | `3` | Invalid arguments, tool error, or unaccepted incomplete coverage |
61
+
62
+ Status is calculated after rule selection, ignore comments, and baselines. Findings take precedence over incomplete coverage; `--best-effort` never changes `1` or `2`.
81
63
 
82
- Codes apply after rule selection, ignore markers, and baselines. Findings take precedence over incompleteness; `--best-effort` does not change `1` or `2`.
64
+ ## CLI
83
65
 
84
- ### Changed-file view and reports
66
+ `npx canship [path] [options]`
85
67
 
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`.
68
+ | Option | Effect |
69
+ |---|---|
70
+ | `-a`, `--all` | Include `likely` findings |
71
+ | `--verbose` | Expand terminal findings |
72
+ | `--report[=file]` | Write HTML; default `canship-report.html` |
73
+ | `--open` | Open `--report` output; disabled in CI and non-interactive shells |
74
+ | `--json` | Print JSON |
75
+ | `--sarif[=file]` | Write SARIF 2.1.0; default `canship.sarif` |
76
+ | `--fix-prompt` | Print repair instructions and separate manual actions |
77
+ | `--no-excerpts` | Remove excerpts from all reports |
78
+ | `--changed-since=ref` | Show changed-file findings; preserve full-scan status |
79
+ | `--only=ids` / `--skip=ids` | Select/exclude rules or namespaces; comma-separated, repeatable |
80
+ | `--list-rules` | List rules without scanning; supports `--json` |
81
+ | `--baseline[=file]` / `--baseline-write[=file]` | Suppress/record findings; default `canship-baseline.json` |
82
+ | `--no-config` / `--no-ignore-markers` | Ignore project configuration/source suppression comments |
83
+ | `--best-effort` | Allow incomplete coverage with no findings to exit `0` |
84
+ | `-h`, `--help` / `-v`, `--version` | Show help/version |
87
85
 
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.
86
+ `--json` and `--fix-prompt` are mutually exclusive; either supports HTML and SARIF output.
89
87
 
90
- SARIF includes execution diagnostics and evidence locations. `--list-rules --json` returns a separate `kind: "rule-catalog"` document.
88
+ `--changed-since` compares the local merge base with the working tree, including non-ignored untracked files. It does not fetch or narrow scan scope. Missing Git, refs, or shared history exits `3`; it cannot be combined with `--baseline-write`.
91
89
 
92
90
  ## Configuration
93
91
 
94
- `canship.config.json` in the scan directory accepts `baseline`, `only`, `skip`, and `all`:
92
+ `canship.config.json` accepts `baseline`, `only`, `skip`, and `all`. CLI options take precedence; `only` and `skip` are mutually exclusive.
95
93
 
96
94
  ```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
95
+ { "skip": ["cors/wildcard-with-credentials"], "all": false }
126
96
  ```
127
97
 
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.
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
- ```
98
+ A standalone `canship-ignore-file` comment excludes a file. `canship-ignore-next-line [rule]` suppresses the next line, optionally for one rule. Exclusions are disclosed and may reduce status to `0` without marking coverage incomplete. For untrusted projects, use `--no-config --no-ignore-markers`.
143
99
 
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.
100
+ Baselines accept existing findings without fixing them. Format v2 tolerates line moves but reports credential changes.
145
101
 
146
- `summarize()` returns finding counts, `partial`, and the default CLI exit code. `listRules()` returns an independent catalog copy.
102
+ Default paths are relative to the scan directory; explicit paths are relative to the working directory. Read/write modes are mutually exclusive. Missing, invalid, or v1 baselines exit `3`. A successful write exits `0`, with a warning for incomplete or selective scans.
147
103
 
148
104
  ## GitHub Action
149
105
 
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.
106
+ Save as `.github/workflows/canship.yml`:
151
107
 
152
108
  ```yaml
153
109
  name: canship
@@ -162,47 +118,56 @@ jobs:
162
118
  with:
163
119
  fetch-depth: 0
164
120
  persist-credentials: false
165
- - uses: Tasomei/canship@dfc17be52684314c8631d665074c133bf1170888
121
+ - uses: Tasomei/canship@97c14d1f1e494a49adf716c455b597edf6ae1d88
166
122
  with:
167
- version: '0.5.0'
123
+ version: '0.6.0'
168
124
  honor-ignore-markers: false
169
125
  ```
170
126
 
171
- The commit pins the wrapper; `version` selects the npm scanner, not repository source. This pinned wrapper defaults to 0.4.0.
127
+ The commit hash pins the Action implementation; `version` selects the npm scanner, not development-branch source. The Action uses Node.js 22, does not install or run project dependencies, and writes a counts-only summary.
172
128
 
173
129
  | Input | Default | Meaning |
174
130
  |---|---|---|
175
- | `version` | `0.4.0` | Exact npm version; no ranges or tags |
131
+ | `version` | `0.5.0` | Exact npm scanner version |
176
132
  | `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
133
 
181
- Path, rule-selection, baseline, and category inputs are documented in [action.yml](./action.yml).
134
+ Incomplete scans and tool errors always fail. Project configuration and SARIF upload are disabled by default. Inputs and outputs: [action.yml](./action.yml).
135
+
136
+ SARIF upload requires `security-events: write` and code scanning support; fork PRs may lack permission. Review reports before upload. Use `pull_request`, not `pull_request_target`, for untrusted PRs.
137
+
138
+ ## API and structured output
139
+
140
+ ```js
141
+ import { scan, summarize } from 'canship'
142
+
143
+ const result = await scan('./my-app', { noExcerpts: true })
144
+ console.log(summarize(result))
145
+ ```
182
146
 
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`.
147
+ `scan()` returns all confidence levels. Options: `only`, `skip`, `honorIgnoreMarkers` (default `true`), `noExcerpts` (default `false`). It does not load configuration, apply baselines, write reports, or set process exit status. Invalid arguments throw. `listRules()` returns the rule catalogue.
184
148
 
185
- 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.
149
+ JSON uses [schemaVersion 1](./schemas/scan-report-v1.schema.json). Check `partial`, `errors`, `skipped`, and `filesScanned` independently of exit status. SARIF includes evidence locations and execution diagnostics.
186
150
 
187
151
  ## Privacy and limits
188
152
 
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.
153
+ - Static checks may miss issues or flag intentional configurations. Business authorisation, rate limiting, dependency vulnerabilities, and deployed settings are not verified.
154
+ - Redaction covers recognised formats only. Unknown secrets may remain in excerpts; `--no-excerpts` omits excerpts. Paths, names, and baseline descriptions remain visible.
155
+ - Google/Firebase/Maps `AIza…` keys are treated as public identifiers, not leak evidence alone. Supabase checks use local migrations and supported bucket configuration.
156
+ - Evaluation snapshot downloads and optional SARIF uploads may use the network.
157
+ - Symbolic links are not followed; nested repositories and submodules need separate scans. In-scope skipped paths and analysis limits mark coverage incomplete. Dependency and build directories excluded by default do not count as coverage gaps.
194
158
 
195
159
  | Limit | Bound |
196
160
  |---|---|
197
- | File reads, including probes | 2 MiB per file; 128 MiB and 10,000 files per scan |
161
+ | File reads | 2 MiB per file; 128 MiB and 10,000 files per scan, including probes |
198
162
  | Directory discovery | 50,000 entries; 16 levels |
199
163
  | Findings | 100 per file, prioritising severity and confidence |
200
- | Git history | 100 relevant revisions per file; 30 seconds per Git command |
164
+ | Git history | 100 relevant revisions per file; 30 seconds per command |
201
165
  | 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 |
166
+ | Identity/control flow | 8 value hops; 4,000 expression characters; 512 assignments/regions per function; 8 nested regions |
167
+ | Request-input tracking | 8 value hops; 512 assignments/regions; 4,000 expression characters; 8 URL-analysis levels; 200 static-prefix characters |
203
168
  | Supabase policy/bucket parsing | 4,000 characters per statement |
204
169
 
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.
170
+ Evidence traces are capped at 24 steps and disclose truncation.
206
171
 
207
172
  ## Development
208
173
 
@@ -218,14 +183,12 @@ npm run prepublishOnly
218
183
  npm run test:package
219
184
  ```
220
185
 
221
- New rules require positive and negative [fixtures](./test/fixtures/). Run the offline corpus:
222
-
223
186
  ```powershell
224
187
  npm run evaluate
225
188
  ```
226
189
 
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.
190
+ New rules require positive and negative [fixtures](./test/fixtures/). Pinned project evaluation: [manifest](./test/evaluation/projects.json), [fetch script](./scripts/fetch-evaluation-projects.mjs), [evaluator](./scripts/evaluate-projects.ts). Passing samples do not establish real-world detection rates.
228
191
 
229
192
  ## License
230
193
 
231
- [MIT](./LICENSE). Supabase/Firebase fixtures retain Apache-2.0; Next.js/`cors` fixtures retain MIT. Sources and licences accompany the fixtures.
194
+ [MIT](./LICENSE). Supabase/Firebase fixtures retain Apache-2.0; Next.js/`cors` fixtures retain MIT.