canship 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 canship contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # canship
2
+
3
+ canship is a read-only static scanner for JavaScript and TypeScript web projects. It detects exposed credentials, misuse of public environment variables, and access-control misconfigurations in Next.js, Vite, Nuxt, Create React App, Expo, Supabase, and Firebase projects.
4
+
5
+ The scan does not execute project code, upload content, or initiate network requests. The npm package has no runtime dependencies. When scanning a Git repository, canship reads only the local working tree and local commit history.
6
+
7
+ ```bash
8
+ npx canship
9
+ ```
10
+
11
+ Node.js 18 or later is required. Git is optional; only commit-history checks require a local Git installation and a readable repository. If canship is not already in the npm cache, `npx` may download it from the npm registry before the scan starts; that download is performed by npm and is not part of the scan.
12
+
13
+ [简体中文](./README.zh-CN.md)
14
+
15
+ ## Checks
16
+
17
+ | Check | Risk | Severity |
18
+ |---|---|---|
19
+ | Hardcoded credentials | A scanned file contains a recognised OpenAI, Anthropic, AWS, Stripe, GitHub, Slack, SendGrid, private-key, or database credential | P0 |
20
+ | Private values in public environment variables | The value may be included in browser-delivered code | P0 |
21
+ | Supabase `service_role` key reachable from client code | A `service_role` key can bypass Row Level Security (RLS) policies | P0 |
22
+ | Git-tracked `.env` files | Recognised credentials remain in repository history; substantial unrecognised values may also produce a lower-confidence finding | P0 |
23
+ | Supabase tables without RLS | A table exposed through the Supabase Data API lacks row-level access control | P1 |
24
+ | Firebase rules allowing unconditional access | Unauthorised clients may be able to read or write data | P1 |
25
+ | Next.js API routes accessing data without authentication | An unverified caller may access data or perform administrative operations | P0 / P1 |
26
+ | CORS reflecting `Origin` while allowing credentials | Another site may access an endpoint with the user's credentials and read the response | P1 |
27
+
28
+ Severity (P0, P1, P2) describes potential impact. Confidence (`certain`, `likely`) describes how strongly the code supports the finding. A `certain` P0/P1 finding blocks release and exits with status `1`; all other findings exit with status `2`.
29
+
30
+ `Access-Control-Allow-Origin: *` combined with credentials is reported at P2 because browsers reject that configuration. A bare wildcard is not reported.
31
+
32
+ For Git-tracked `.env` files, a recognised credential is reported at `certain` confidence. A substantial value that is not recognised but does not look public or placeholder-like is reported at `likely` confidence. Environment templates, public values, placeholders, and short settings do not trigger this Git rule solely because the file is tracked.
33
+
34
+ ## Usage
35
+
36
+ ```bash
37
+ npx canship [path]
38
+ ```
39
+
40
+ The current directory is scanned when no path is provided.
41
+
42
+ | Option | Description |
43
+ |---|---|
44
+ | `-a`, `--all` | Show `likely` findings |
45
+ | `--json` | Write machine-readable JSON |
46
+ | `--fix-prompt` | Write remediation instructions for a coding assistant |
47
+ | `--report[=file]` | Write a self-contained HTML report; defaults to `canship-report.html` |
48
+ | `--best-effort` | Allow exit `0` when the scan is incomplete and has no findings; does not change the status of existing findings |
49
+ | `-h`, `--help` | Show help |
50
+ | `-v`, `--version` | Show the version |
51
+
52
+ `--json` and `--fix-prompt` are mutually exclusive. `--report` writes a separate file and may be combined with either mode.
53
+
54
+ ### Exit status
55
+
56
+ | Status | Meaning |
57
+ |---|---|
58
+ | `0` | No findings at any confidence and the scan completed; with `--best-effort`, it may also mean an incomplete scan was accepted |
59
+ | `1` | At least one `certain` P0/P1 finding |
60
+ | `2` | Findings exist, but none is a confirmed P0/P1 release blocker |
61
+ | `3` | Invalid arguments, a tool error, or an incomplete scan without `--best-effort` |
62
+
63
+ When findings and an incomplete scan coexist, status `1` or `2` takes precedence. The JSON fields `partial`, `errors`, and `skipped` still preserve the incomplete-scan state.
64
+
65
+ The default view expands only `certain` findings. Hidden `likely` findings still produce status `2`; the terminal and HTML report display a warning, and JSON reports the count in `hiddenLikely`. Use `--all` to include their full details.
66
+
67
+ ### Excluding a file
68
+
69
+ Add `canship-ignore-file` on a line by itself to exclude the entire file. The marker may be wrapped only in `//`, `#`, `--`, `*`, `/* */`, or `<!-- -->` comment syntax. Intentionally excluded files are listed in the report and do not make the scan incomplete.
70
+
71
+ ### Remediation instructions
72
+
73
+ `--fix-prompt` separates code changes that a coding assistant can perform from actions that require the project maintainer, such as rotating credentials, changing provider settings, or rewriting Git history.
74
+
75
+ ## Rule activation
76
+
77
+ | Rule | Activation conditions |
78
+ |---|---|
79
+ | Public environment variables | The name starts with `NEXT_PUBLIC_`, `VITE_`, `REACT_APP_`, `EXPO_PUBLIC_`, `NUXT_PUBLIC_`, `GATSBY_`, `VUE_APP_`, or `PUBLIC_` |
80
+ | Git-tracked environment files | The target is inside a readable local Git repository whose `.git` metadata is inside the checkout or names it back; templates, public values, placeholders, and short settings are excluded, and at most the 100 most recent relevant revisions of each file are inspected |
81
+ | Next.js API authentication | Only data-accessing handlers under `app/api/**` and `pages/api/**`; recognises enforcing authentication calls, identity conditions that control rejection, and middleware `matcher` coverage for the route |
82
+ | Supabase RLS | The current project scope contains `supabase/`, an `@supabase/supabase-js` or `@supabase/ssr` import, or a `SUPABASE_URL`; table-related DDL is replayed across migrations within that project scope |
83
+ | Other credential and configuration rules | Match known file formats and content patterns without requiring a specific front-end framework |
84
+
85
+ ## Known limitations
86
+
87
+ - canship uses static heuristics and does not verify runtime behaviour. Custom authentication wrappers, dynamic configuration, and unsupported syntax can cause false positives or false negatives.
88
+ - Detection and redaction use the same credential patterns. A credential that canship cannot recognise cannot be guaranteed to be masked; if another rule quotes the same line, the original value may appear in the report. Treat reports as internal material.
89
+ - Entropy-based detection is intentionally not used because random identifiers, hashes, and ordinary Base64 text cannot be classified reliably from entropy alone.
90
+ - Files are limited to 2 MiB, traversal to 16 directory levels, and output to 100 findings per file. Git history checks inspect at most the 100 most recent relevant revisions of each file. Reaching a limit is recorded explicitly.
91
+ - Symbolic links are not followed and make the scan incomplete.
92
+ - Nested Git repositories and submodules are not expanded by the parent scan. They are listed as skipped and the parent scan is marked incomplete.
93
+ - A `.git` file may point outside the checkout. canship follows it only when the target names this checkout back, which is what git records for a linked worktree and for a submodule. Anything else is read as a redirect to an unrelated repository: file scanning continues, history checks are marked incomplete.
94
+ - Google, Firebase, and Maps `AIza...` keys are treated as public identifiers. Their application and API restrictions exist in Google Cloud and cannot be verified from local source, so the key value alone is not reported as a credential leak.
95
+ - Rate limiting, injection, dependency vulnerabilities, and business authorisation beyond caller authentication are outside the scan scope.
96
+
97
+ A canship result describes only what the implemented rules observed in the files that were read. It is not proof that the project has no other security defects.
98
+
99
+ ## Planned
100
+
101
+ `--probe` is not implemented. The planned mode would, after explicit confirmation, issue read-only verification requests to service endpoints found in local project configuration. The current release does not initiate network requests.
102
+
103
+ ## Contributing
104
+
105
+ Every new or changed detection rule should include at least two fixtures: one that must be reported and one that must not. See [`test/fixtures/`](./test/fixtures/).
106
+
107
+ ```bash
108
+ npm ci
109
+ npm run prepublishOnly
110
+ ```
111
+
112
+ ## License
113
+
114
+ [MIT](./LICENSE)
@@ -0,0 +1,116 @@
1
+ # canship
2
+
3
+ canship 是面向 JavaScript/TypeScript Web 项目的只读静态扫描工具,用于发现凭据泄露、公开环境变量误用和访问控制配置错误。支持 Next.js、Vite、Nuxt、Create React App、Expo,以及使用 Supabase 或 Firebase 的项目。
4
+
5
+ 扫描过程不会执行被扫描项目的代码,不会上传内容或主动访问网络,也没有运行时 npm 依赖。扫描 Git 仓库时,只读取本地工作区和本地提交历史。
6
+
7
+ ```bash
8
+ npx canship
9
+ ```
10
+
11
+ 要求 Node.js 18 或更高版本。项目无需使用 Git;只有提交历史检查依赖本机 Git 和可读取的本地仓库。npm 缓存中没有 canship 时,`npx` 可能先从 npm registry 下载软件包,这一步由 npm 执行,不属于扫描过程。
12
+
13
+ [English](./README.md)
14
+
15
+ ## 检测范围
16
+
17
+ | 检查项 | 风险 | 级别 |
18
+ |---|---|---|
19
+ | 硬编码凭据 | 当前扫描文件中包含已识别的 OpenAI、Anthropic、AWS、Stripe、GitHub、Slack、SendGrid、私钥或数据库连接凭据 | P0 |
20
+ | 公开环境变量中的私密值 | 私密值可能被打包进浏览器代码 | P0 |
21
+ | 客户端可访问 Supabase `service_role` key | `service_role` 可绕过 Row Level Security(RLS)策略 | P0 |
22
+ | Git 跟踪的 `.env` 文件 | 已识别凭据会留在仓库历史中;未识别但内容仍像真实配置的文件也可能产生低置信度结果 | P0 |
23
+ | Supabase 表未启用 RLS | 表经 Supabase Data API 暴露时缺少行级访问控制 | P1 |
24
+ | Firebase 规则允许无条件访问 | 未经授权的客户端可能读取或写入数据 | P1 |
25
+ | Next.js API route 访问数据但未鉴权 | 未验证调用方即可访问数据或执行管理操作 | P0 / P1 |
26
+ | CORS 回显 `Origin` 且允许凭据 | 其他站点可能携带用户凭据访问接口并读取响应 | P1 |
27
+
28
+ 严重度(P0、P1、P2)表示潜在影响,置信度(`certain`、`likely`)表示规则对结论的确定程度。两者相互独立:`certain` 的 P0/P1 结果会阻止发布并返回退出码 `1`;其他结果返回退出码 `2`。
29
+
30
+ `Access-Control-Allow-Origin: *` 与凭据同时出现时报告为 P2,因为浏览器会拒绝该组合;单独使用 `*` 不报告。
31
+
32
+ 对于 Git 跟踪的 `.env` 文件,已识别凭据按 `certain` 报告;未识别但值较长且不像公开值或占位符时按 `likely` 报告。环境变量模板、公开值、占位符和短设置不会仅因文件进入 Git 而触发这条规则。
33
+
34
+ ## 使用方式
35
+
36
+ ```bash
37
+ npx canship [路径]
38
+ ```
39
+
40
+ 未提供路径时扫描当前目录。
41
+
42
+ | 参数 | 说明 |
43
+ |---|---|
44
+ | `-a`, `--all` | 显示 `likely` 结果 |
45
+ | `--json` | 输出机器可读的 JSON |
46
+ | `--fix-prompt` | 输出可交给编程助手的修复说明 |
47
+ | `--report[=文件]` | 生成自包含的 HTML 报告;默认写入 `canship-report.html` |
48
+ | `--best-effort` | 扫描不完整且没有任何结果时允许退出 `0`;不会改变已有结果对应的退出码 |
49
+ | `-h`, `--help` | 显示帮助 |
50
+ | `-v`, `--version` | 显示版本 |
51
+
52
+ `--json` 与 `--fix-prompt` 互斥。`--report` 写入独立文件,可以与其中任意一种组合。
53
+
54
+ ### 退出码
55
+
56
+ | 退出码 | 含义 |
57
+ |---|---|
58
+ | `0` | 没有任何置信度的结果,且扫描完整;使用 `--best-effort` 时也可能表示接受了不完整扫描 |
59
+ | `1` | 至少有一条 `certain` 的 P0/P1 结果 |
60
+ | `2` | 存在结果,但没有已确认的 P0/P1 阻断项 |
61
+ | `3` | 参数或工具错误,或者扫描不完整且未使用 `--best-effort` |
62
+
63
+ 如果结果与不完整扫描同时存在,退出码 `1` 或 `2` 优先。JSON 中的 `partial`、`errors` 和 `skipped` 字段仍会保留扫描不完整的信息。
64
+
65
+ 默认视图只展开 `certain` 结果。被隐藏的 `likely` 结果仍会使进程返回 `2`;终端和 HTML 报告会显示警告,JSON 通过 `hiddenLikely` 给出数量。使用 `--all` 可查看完整结果。
66
+
67
+ ### 排除文件
68
+
69
+ 在文件中单独添加一行 `canship-ignore-file` 可以排除整个文件。标记前后只允许出现 `//`、`#`、`--`、`*`、`/* */` 或 `<!-- -->` 注释符。被主动排除的文件会列入报告,但不会使扫描标记为不完整。
70
+
71
+ ### 修复说明
72
+
73
+ `--fix-prompt` 将输出分为两部分:可以交给编程助手执行的代码修改,以及必须由项目维护者完成的操作,例如轮换密钥、修改服务端控制台配置或重写 Git 历史。
74
+
75
+ 当前扫描结果、HTML 报告和修复说明正文使用英文,JSON 字段名也使用英文。
76
+
77
+ ## 规则生效条件
78
+
79
+ | 规则 | 生效条件 |
80
+ |---|---|
81
+ | 公开环境变量 | 变量名以 `NEXT_PUBLIC_`、`VITE_`、`REACT_APP_`、`EXPO_PUBLIC_`、`NUXT_PUBLIC_`、`GATSBY_`、`VUE_APP_` 或 `PUBLIC_` 开头 |
82
+ | Git 凭据历史 | 目标位于可读取的本地 Git 仓库中,且 `.git` 元数据位于工作区内、或其目标反向指认本工作区;模板、公开值、占位符和短设置除外;每个文件最多检查最近 100 个相关版本 |
83
+ | Next.js API 鉴权 | 仅检查 `app/api/**` 和 `pages/api/**`;识别明确的鉴权调用、控制拒绝响应的身份条件,以及覆盖该路由的 middleware `matcher` |
84
+ | Supabase RLS | 当前子项目存在 `supabase/`、`@supabase/supabase-js`/`@supabase/ssr` 导入或 `SUPABASE_URL`;按子项目重放 migration 中与表相关的 DDL |
85
+ | 其他凭据和配置规则 | 按已知文件格式和内容模式匹配,不要求特定前端框架 |
86
+
87
+ ## 已知限制
88
+
89
+ - canship 是静态启发式扫描器,不验证运行时行为。自定义鉴权封装、动态配置和非标准语法可能产生误报或漏报。
90
+ - 检测和脱敏使用同一套凭据特征。无法识别的凭据也无法保证脱敏;如果其所在行因其他规则被引用,原始值可能出现在报告中。因此报告应视为内部材料。
91
+ - 工具不使用熵值检测,避免将随机 ID、哈希或普通 Base64 文本误判为凭据。
92
+ - 单个文件最大读取 2 MiB,目录最大扫描深度为 16 层,单文件最多报告 100 条结果,Git 历史中单文件最多检查最近 100 个版本。触及上限时扫描会明确记录。
93
+ - 符号链接不会被跟随,并会使扫描标记为不完整。
94
+ - 嵌套 Git 仓库和子模块不会由父项目展开扫描。父项目会记录跳过项并标记扫描不完整。
95
+ - `.git` 文件可能指向工作区之外。canship 只在目标反过来指认本工作区时才跟随——这正是 git 为链接工作树和子模块记录的形式;其余情况按"重定向到无关仓库"处理:文件扫描继续,历史检查标记为不完整。
96
+ - Google/Firebase/Maps 的 `AIza...` key 被视为公开标识符。其应用和 API 限制保存在 Google Cloud,无法仅从本地源码判断,因此不会仅凭该值报告凭据泄露。
97
+ - 不检查限流、注入、依赖漏洞,也不验证“调用方是否已登录”之外的业务授权逻辑。
98
+
99
+ 扫描结果只表示已实现规则在已读取文件中观察到的情况,不能证明项目不存在其他安全问题。
100
+
101
+ ## 计划
102
+
103
+ `--probe` 尚未实现。计划在用户显式确认后,对项目本地配置中的服务地址发起只读验证请求。当前版本不会联网。
104
+
105
+ ## 参与开发
106
+
107
+ 新增或修改检测规则时,至少提供两个测试夹具:一个应当被检出,一个不应被检出。参见 [`test/fixtures/`](./test/fixtures/)。
108
+
109
+ ```bash
110
+ npm ci
111
+ npm run prepublishOnly
112
+ ```
113
+
114
+ ## 许可
115
+
116
+ [MIT](./LICENSE)