create-dwy 0.25.3 → 0.27.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/CHANGELOG.md +20 -0
- package/package.json +1 -1
- package/src/sync-all.js +46 -2
- package/src/sync-plan.js +9 -1
- package/templates/ai-tools/CHANGELOG.md +17 -0
- package/templates/ai-tools/VERSION +1 -1
- package/templates/ai-tools/skills/Apple/dwy-apple-appkit/SKILL.md +17 -0
- package/templates/ai-tools/skills/Apple/dwy-apple-architecture/SKILL.md +12 -0
- package/templates/ai-tools/skills/Apple/dwy-apple-architecture/references/extraction.md +9 -0
- package/templates/ai-tools/{rules/iOS/dwy-apple-layering.md → skills/Apple/dwy-apple-architecture/references/layering.md} +2 -10
- package/templates/ai-tools/skills/Apple/dwy-apple-architecture/references/pitfalls.md +3 -0
- package/templates/ai-tools/skills/Apple/dwy-ios-development/SKILL.md +14 -0
- package/templates/ai-tools/skills/Apple/dwy-ios-development/references/appearance.md +6 -0
- package/templates/ai-tools/{rules/iOS/dwy-ios-layout-adaptation.md → skills/Apple/dwy-ios-development/references/layout.md} +0 -6
- package/templates/ai-tools/{rules/iOS/dwy-ios-pitfalls.md → skills/Apple/dwy-ios-development/references/pitfalls.md} +1 -9
- package/templates/ai-tools/skills/Apple/dwy-ios-development/references/presentations.md +11 -0
- package/templates/ai-tools/skills/Apple/dwy-macos-development/SKILL.md +14 -0
- package/templates/ai-tools/skills/Apple/dwy-macos-development/references/pitfalls.md +9 -0
- package/templates/ai-tools/skills/Apple/dwy-macos-development/references/presentations.md +5 -0
- package/templates/ai-tools/skills/Apple/dwy-macos-development/references/ui-adaptation.md +6 -0
- package/templates/ai-tools/skills/Apple/dwy-swift-development/SKILL.md +16 -0
- package/templates/ai-tools/{rules/iOS/dwy-swift-core.md → skills/Apple/dwy-swift-development/references/conventions.md} +2 -8
- package/templates/ai-tools/skills/Apple/dwy-swift-development/references/pitfalls.md +5 -0
- package/templates/ai-tools/skills/Apple/dwy-swift-development/references/presentations.md +8 -0
- package/templates/ai-tools/skills/Python/dwy-apple-notify/SKILL.md +24 -0
- package/templates/ai-tools/skills/Python/dwy-apple-notify/references/v0/0.1.6.md +81 -0
- package/templates/ai-tools/skills/Python/dwy-auth/SKILL.md +24 -0
- package/templates/ai-tools/skills/Python/dwy-auth/references/v0/0.2.0.md +163 -0
- package/templates/ai-tools/skills/Python/dwy-device-fingerprint/SKILL.md +19 -0
- package/templates/ai-tools/skills/Python/dwy-device-fingerprint/references/v0/0.1.1.md +90 -0
- package/templates/ai-tools/skills/Python/dwy-eapi/SKILL.md +1 -1
- package/templates/ai-tools/skills/Python/dwy-eapi/versions.yml +3 -3
- package/templates/ai-tools/skills/Python/dwy-email-notify/SKILL.md +19 -0
- package/templates/ai-tools/skills/Python/dwy-email-notify/references/v0/0.1.0.md +31 -0
- package/templates/ai-tools/skills/Python/dwy-notify/SKILL.md +24 -0
- package/templates/ai-tools/skills/Python/dwy-notify/references/v0/0.3.2.md +128 -0
- package/templates/ai-tools/skills/Python/dwy-payment/SKILL.md +19 -0
- package/templates/ai-tools/skills/Python/dwy-payment/references/v0/0.3.0.md +90 -0
- package/templates/ai-tools/skills/Python/dwy-python-api/SKILL.md +8 -0
- package/templates/ai-tools/skills/Python/dwy-python-architecture/SKILL.md +8 -0
- package/templates/ai-tools/skills/Python/dwy-python-development/SKILL.md +17 -0
- package/templates/ai-tools/{rules/Python → skills/Python/dwy-python-development/references}/dwy-python-base-lib.md +3 -3
- package/templates/ai-tools/skills/Python/dwy-webhooks/SKILL.md +24 -0
- package/templates/ai-tools/skills/Python/dwy-webhooks/references/v0/0.6.2.md +238 -0
- package/templates/ai-tools/skills/Python/dwy-wechatpay-notify/SKILL.md +19 -0
- package/templates/ai-tools/skills/Python/dwy-wechatpay-notify/references/v0/0.1.1.md +101 -0
- package/templates/ai-tools/skills/Vue/dwy-admin-kit/SKILL.md +12 -0
- package/templates/ai-tools/skills/Vue/dwy-admin-kit/references/v0/0.3.0.md +176 -0
- package/templates/ai-tools/skills/Vue/dwy-auth-web/SKILL.md +10 -0
- package/templates/ai-tools/skills/Vue/dwy-ekit/SKILL.md +2 -3
- package/templates/ai-tools/skills/Vue/dwy-eui/SKILL.md +1 -1
- package/templates/ai-tools/skills/Vue/dwy-eui/versions.yml +2 -2
- package/templates/ai-tools/skills/Vue/dwy-vue-architecture/SKILL.md +8 -0
- package/templates/ai-tools/skills/Vue/dwy-vue-development/SKILL.md +16 -0
- package/templates/ai-tools/{rules/Vue → skills/Vue/dwy-vue-development/references}/dwy-vue-base-lib.md +3 -3
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +6 -6
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-commercial-sdk/SKILL.md +20 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +6 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +2 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +2 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +2 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +2 -2
- package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-dependency-choice/SKILL.md +2 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/SKILL.md +0 -244
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/files-whitelist-snippet.json +0 -22
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/js-commercial.npmignore +0 -76
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/python-commercial-MANIFEST.in +0 -37
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/assets/python-commercial.gitignore +0 -62
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/js-commercial-guide.md +0 -133
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/publish-blacklist.md +0 -106
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/python-commercial-guide.md +0 -147
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/references/sensitive-patterns.md +0 -112
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_comments.py +0 -260
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_js_publish.sh +0 -110
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/check_python_publish.sh +0 -171
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/detect_sdk.sh +0 -71
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/init_config.sh +0 -91
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +0 -136
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/references/semver-spec.md +0 -70
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/scripts/bump.py +0 -137
- /package/templates/ai-tools/{rules/Python → skills/Python/dwy-python-api/references}/dwy-python-backend.md +0 -0
- /package/templates/ai-tools/{rules/Python → skills/Python/dwy-python-architecture/references}/dwy-python-layering.md +0 -0
- /package/templates/ai-tools/{rules/Python → skills/Python/dwy-python-development/references}/dwy-python-core.md +0 -0
- /package/templates/ai-tools/{rules/Python → skills/Python/dwy-python-development/references}/dwy-python-pitfalls.md +0 -0
- /package/templates/ai-tools/{rules/Python → skills/Python/dwy-python-development/references}/dwy-python-testing.md +0 -0
- /package/templates/ai-tools/{rules/Vue → skills/Vue/dwy-vue-architecture/references}/dwy-vue-layering.md +0 -0
- /package/templates/ai-tools/{rules/Vue → skills/Vue/dwy-vue-development/references}/dwy-vue-core.md +0 -0
- /package/templates/ai-tools/{rules/Vue → skills/Vue/dwy-vue-development/references}/dwy-vue-pinia.md +0 -0
- /package/templates/ai-tools/{rules/Vue → skills/Vue/dwy-vue-development/references}/dwy-vue-pitfalls.md +0 -0
- /package/templates/ai-tools/{rules/Vue → skills/Vue/dwy-vue-development/references}/dwy-vue-testing.md +0 -0
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# Python 商业版 SDK 完整打包指南
|
|
2
|
-
|
|
3
|
-
目标:发布到 PyPI 的 wheel 内**不含 `.py` 源文件**,只含编译后的 `.so` 与必要类型存根。
|
|
4
|
-
|
|
5
|
-
## 决策树
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
模块类型
|
|
9
|
-
├── 公开 API 入口(如 mypkg/__init__.py)→ 保留 .py(用户要 import)
|
|
10
|
-
├── 内部实现(_ 开头,如 mypkg/_engine.py)→ 编译为 .so
|
|
11
|
-
└── 类型存根(.pyi)→ 保留(用户 IDE 提示)
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## 工具选择
|
|
15
|
-
|
|
16
|
-
| 工具 | 优势 | 推荐场景 |
|
|
17
|
-
|---|---|---|
|
|
18
|
-
| **Cython** | 成熟、setuptools 集成无缝、编译快 | **默认选择**,纯 Python → .so |
|
|
19
|
-
| Nuitka | 优化更激进、支持整包编译 | 大型业务逻辑、想编译整个包 |
|
|
20
|
-
|
|
21
|
-
下面以 Cython 为准。
|
|
22
|
-
|
|
23
|
-
## 项目结构示例
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
mypkg/ # 包根目录
|
|
27
|
-
├── pyproject.toml
|
|
28
|
-
├── MANIFEST.in
|
|
29
|
-
├── .gitignore
|
|
30
|
-
├── src/
|
|
31
|
-
│ └── mypkg/
|
|
32
|
-
│ ├── __init__.py # 公开入口(保留 .py,re-export)
|
|
33
|
-
│ ├── client.py # 公开 API(保留 .py)
|
|
34
|
-
│ ├── _engine.py # 内部核心(编译为 .so)
|
|
35
|
-
│ ├── _engine.pyi # 类型存根(保留)
|
|
36
|
-
│ ├── _utils.py # 内部工具(编译为 .so)
|
|
37
|
-
│ └── _utils.pyi # 类型存根(保留)
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## pyproject.toml 配置
|
|
41
|
-
|
|
42
|
-
```toml
|
|
43
|
-
[build-system]
|
|
44
|
-
requires = ["setuptools>=68.0", "Cython>=3.0"]
|
|
45
|
-
build-backend = "setuptools.build_meta"
|
|
46
|
-
|
|
47
|
-
[project]
|
|
48
|
-
name = "mypkg"
|
|
49
|
-
version = "1.0.0"
|
|
50
|
-
requires-python = ">=3.11"
|
|
51
|
-
|
|
52
|
-
[tool.setuptools]
|
|
53
|
-
include-package-data = true
|
|
54
|
-
zip-safe = false
|
|
55
|
-
|
|
56
|
-
[tool.setuptools.packages.find]
|
|
57
|
-
where = ["src"]
|
|
58
|
-
|
|
59
|
-
[tool.setuptools.package-data]
|
|
60
|
-
mypkg = ["*.pyi", "py.typed"]
|
|
61
|
-
|
|
62
|
-
[tool.setuptools.exclude-package-data]
|
|
63
|
-
mypkg = [
|
|
64
|
-
"_*.py", # 排除以 _ 开头的源文件(已编译为 .so)
|
|
65
|
-
"*.c", # Cython 中间产物
|
|
66
|
-
"*.pyx", # Cython 源
|
|
67
|
-
"*.pyc",
|
|
68
|
-
"__pycache__/*",
|
|
69
|
-
]
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## setup.py(Cython 入口)
|
|
73
|
-
|
|
74
|
-
```python
|
|
75
|
-
from setuptools import setup
|
|
76
|
-
from Cython.Build import cythonize
|
|
77
|
-
from pathlib import Path
|
|
78
|
-
|
|
79
|
-
# 找所有 _ 开头的 .py 模块(命名约定:内部模块用 _ 前缀)
|
|
80
|
-
internal_modules = [
|
|
81
|
-
str(p) for p in Path("src/mypkg").glob("_*.py")
|
|
82
|
-
if p.name != "__init__.py"
|
|
83
|
-
]
|
|
84
|
-
|
|
85
|
-
setup(
|
|
86
|
-
ext_modules=cythonize(
|
|
87
|
-
internal_modules,
|
|
88
|
-
compiler_directives={"language_level": "3"},
|
|
89
|
-
),
|
|
90
|
-
)
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## MANIFEST.in
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
graft src
|
|
97
|
-
include README.md LICENSE py.typed
|
|
98
|
-
include src/mypkg/*.pyi
|
|
99
|
-
|
|
100
|
-
# 全局排除源码与中间产物
|
|
101
|
-
global-exclude _*.py
|
|
102
|
-
global-exclude *.pyx *.pxd *.c
|
|
103
|
-
global-exclude *.pyc *.pyo
|
|
104
|
-
global-exclude __pycache__ .DS_Store
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
`graft src` 含整个 src/,然后 `global-exclude _*.py` 排掉内部源;公开 `.py` 保留(如 `__init__.py` / `client.py` 不以 `_` 开头)。
|
|
108
|
-
|
|
109
|
-
## 构建命令
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
# 清理旧产物
|
|
113
|
-
rm -rf build/ dist/ src/*.egg-info/
|
|
114
|
-
|
|
115
|
-
# 编译 + 打包
|
|
116
|
-
python -m build
|
|
117
|
-
|
|
118
|
-
# 验证 wheel 内不含 _ 开头的 .py
|
|
119
|
-
unzip -l dist/*.whl | grep -E '_.*\.py$' && echo "FAIL: source leaked" || echo "OK"
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## 验收清单(流程 B 用)
|
|
123
|
-
|
|
124
|
-
| 检查项 | 命令 |
|
|
125
|
-
|---|---|
|
|
126
|
-
| pyproject.toml 含 Cython | `grep -E 'cython\|Cython' pyproject.toml` |
|
|
127
|
-
| `_*.so` 存在 | `find src -name '_*.so' -type f` 应该有输出 |
|
|
128
|
-
| MANIFEST.in 排除源码 | `grep -E 'global-exclude.*_\*\.py' MANIFEST.in` |
|
|
129
|
-
| wheel 内无 `_*.py` | `unzip -l dist/*.whl \| grep -E '_.*\.py$'` 应该为空 |
|
|
130
|
-
| .gitignore 含编译产物 | `grep -E '^\*\.so\b\|^build/\b\|^dist/\b' .gitignore` |
|
|
131
|
-
|
|
132
|
-
## 常见踩坑
|
|
133
|
-
|
|
134
|
-
| 现象 | 原因 | 修复 |
|
|
135
|
-
|---|---|---|
|
|
136
|
-
| wheel 里有 `.py` 源 | `exclude-package-data` 没匹配到 / MANIFEST.in 缺 global-exclude | 同时配 setup 配置 + MANIFEST.in |
|
|
137
|
-
| 装完后 `ImportError: cannot import name '_engine'` | `__init__.py` 用了 `from . import _engine` 但 .so 命名空间不对 | 确保 `.so` 文件在包内,名字与 import 路径一致 |
|
|
138
|
-
| `pip install -e .` 后 import 慢 | 开发模式没编译 .so,跑了 .py | 开发用 `python setup.py build_ext --inplace` 触发编译 |
|
|
139
|
-
| 跨平台 wheel(manylinux / macOS) | 单平台编译产物只能装到对应平台 | 用 `cibuildwheel`(项目已有 dwy-github-action-publish skill) |
|
|
140
|
-
|
|
141
|
-
## 参考 dwy-shared 内已有 SDK
|
|
142
|
-
|
|
143
|
-
`backend/`(`dwyeapi`)当前是**开源版**(pyproject.toml 无 Cython 配置)。若要转商业版:
|
|
144
|
-
1. 把 `src/dwyeapi/security.py` / `database.py` 重命名为 `_security.py` / `_database.py`
|
|
145
|
-
2. `__init__.py` 加 re-export 维持公开 API
|
|
146
|
-
3. 加 Cython 配置、MANIFEST.in、setup.py
|
|
147
|
-
4. 重新发布 → 装完后用户 import 路径不变,但拿不到 .py 源
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
# SDK 接口注释敏感模式清单
|
|
2
|
-
|
|
3
|
-
`check_comments.py` 的规则源。SDK 对外注释(Python docstring、JSDoc、TSDoc)**只写**功能含义、入参格式、返回值、异常,下列模式一律视为泄露。
|
|
4
|
-
|
|
5
|
-
## 模式表
|
|
6
|
-
|
|
7
|
-
| 模式 ID | 正则 | 风险 | 误报豁免(如有) |
|
|
8
|
-
|---|---|---|---|
|
|
9
|
-
| `internal-domain` | `[\w-]+\.(internal\|corp\|local\|intranet)\b` | 暴露内网域名结构 | 出现在 example.com / your-domain 等示例语 → 视为示例 |
|
|
10
|
-
| `private-ip` | `\b(10\.\d+\.\d+\.\d+\|192\.168\.\d+\.\d+\|172\.(1[6-9]\|2\d\|3[01])\.\d+\.\d+)\b` | 暴露内网 IP | — |
|
|
11
|
-
| `sql-statement` | `\b(SELECT\|UPDATE\|DELETE\|INSERT)\b[^.]*\b(FROM\|WHERE\|SET\|INTO)\b` | 暴露表结构、查询逻辑 | 行含 `example`/`e.g.` 视作示例语 |
|
|
12
|
-
| `redis-key-pattern` | `\b(cache\|redis\|fmt\|lock\|queue):[a-z_]+:` | 暴露缓存键 schema | — |
|
|
13
|
-
| `jira-ticket` | `\b[A-Z]{2,6}-\d{2,6}\b` | 暴露内部工单系统 | 行含「示例」「example」豁免 |
|
|
14
|
-
| `internal-service` | `\b[a-z_]+_(service\|server\|grpc\|rpc)_(v\d\|prod\|stg\|dev)\b` | 暴露内部服务命名 | — |
|
|
15
|
-
| `service-port` | `:(\d{4,5})\b` 且行内含 `connect\|server\|host\|endpoint\|address` | 暴露内网服务端口 | 默认端口 80/443/8080 豁免,example 示例豁免 |
|
|
16
|
-
| `internal-email` | `[\w.+-]+@(?!example\.com\|gmail\.com\|outlook\.com)[\w.-]+\.[a-z]{2,}` | 暴露员工邮箱 | 公开公司域名(github.io 等)豁免 |
|
|
17
|
-
| `aws-arn` | `arn:aws:[a-z0-9-]+:[a-z0-9-]*:\d+:[a-zA-Z0-9-_/:]+` | 暴露 AWS 账号 ID 与资源 | — |
|
|
18
|
-
| `cred-keyword` | `\b(API_KEY\|TOKEN\|SECRET\|PASSWORD\|PRIVATE_KEY)\s*[=:]\s*['"][\w-]+['"]` | 注释里硬编码凭证 | 占位 `xxx` / `<your-token>` 豁免 |
|
|
19
|
-
| `aws-key` | `\bAKIA[0-9A-Z]{16}\b` | AWS Access Key ID | — |
|
|
20
|
-
|
|
21
|
-
## 正例 vs 反例
|
|
22
|
-
|
|
23
|
-
### Python docstring
|
|
24
|
-
|
|
25
|
-
**反例**:
|
|
26
|
-
|
|
27
|
-
```python
|
|
28
|
-
def get_user(user_id: str):
|
|
29
|
-
"""
|
|
30
|
-
Connect to https://users.internal.corp.com/v2/profile via gRPC.
|
|
31
|
-
Query SELECT * FROM users_prod WHERE id = ? with cache key cache:user:{id}.
|
|
32
|
-
Owner: john.doe@anthropic.io. See PAY-1234 for context.
|
|
33
|
-
"""
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
**正例**:
|
|
37
|
-
|
|
38
|
-
```python
|
|
39
|
-
def get_user(user_id: str):
|
|
40
|
-
"""
|
|
41
|
-
获取用户基本资料。
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
user_id: 用户唯一标识符(UUID 格式)。
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
UserProfile,含 name / email / created_at 字段。
|
|
48
|
-
|
|
49
|
-
Raises:
|
|
50
|
-
UserNotFoundError: user_id 不存在时抛出。
|
|
51
|
-
AuthenticationError: 当前会话无权限读取该用户资料。
|
|
52
|
-
|
|
53
|
-
Example:
|
|
54
|
-
>>> user = get_user("user-123")
|
|
55
|
-
>>> user.name
|
|
56
|
-
'Alice'
|
|
57
|
-
"""
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### TypeScript JSDoc
|
|
61
|
-
|
|
62
|
-
**反例**:
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
/**
|
|
66
|
-
* Fetch user from db.internal.corp:5432 (table users_prod).
|
|
67
|
-
* Uses Redis cache key fmt:user:{id} ttl 3600s.
|
|
68
|
-
* Fallback to backup_db at 10.0.0.5:5432 if primary down.
|
|
69
|
-
* See JIRA USR-456, contact #user-platform on internal Slack.
|
|
70
|
-
*/
|
|
71
|
-
export async function fetchUser(id: string): Promise<UserProfile> { ... }
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
**正例**:
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
/**
|
|
78
|
-
* 获取用户基本资料。
|
|
79
|
-
*
|
|
80
|
-
* @param id 用户唯一标识符(UUID 格式)
|
|
81
|
-
* @returns 用户资料对象,含 name / email / created_at 字段
|
|
82
|
-
* @throws {UserNotFoundError} id 不存在时抛出
|
|
83
|
-
* @throws {AuthenticationError} 当前会话无权限读取该用户资料
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* ```ts
|
|
87
|
-
* const user = await fetchUser("user-123");
|
|
88
|
-
* console.log(user.name);
|
|
89
|
-
* ```
|
|
90
|
-
*/
|
|
91
|
-
export async function fetchUser(id: string): Promise<UserProfile> { ... }
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## 应该写什么 vs 不应该写什么
|
|
95
|
-
|
|
96
|
-
| 类别 | 应该写 | 不应该写 |
|
|
97
|
-
|---|---|---|
|
|
98
|
-
| 接口功能 | 业务语义(「获取用户资料」) | 实现路径(「调用 gRPC service xxx」) |
|
|
99
|
-
| 入参 | 类型、格式、约束(UUID、长度上限) | 上游表字段、内部 schema 字段名 |
|
|
100
|
-
| 返回 | 字段语义 | 来自哪张表、哪个缓存键 |
|
|
101
|
-
| 异常 | 业务异常类、触发条件 | 数据库连接错误细节、内部 RPC 失败码 |
|
|
102
|
-
| 示例 | 公开占位(`user-123` / `example.com`) | 真实用户 ID、真实邮箱、内网域名 |
|
|
103
|
-
| 链接 | 公开文档 URL、相对路径文档 | 内网 wiki、JIRA、Slack、Confluence 链接 |
|
|
104
|
-
|
|
105
|
-
## 行内豁免约定
|
|
106
|
-
|
|
107
|
-
误报场景在该行追加注释:
|
|
108
|
-
|
|
109
|
-
- Python:`# sdk-spec: ignore`
|
|
110
|
-
- TS/JS:`// sdk-spec: ignore`
|
|
111
|
-
|
|
112
|
-
豁免**单行**,不豁免整段。要长期豁免某文件 → 在 `<project_root>/.dwy/sdk-spec/config.json` 加 `ignore_files: ["path/to/file.py"]`(未来支持,当前手动用行内豁免)。
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""扫 Python docstring 与 JS/TS JSDoc 中的敏感模式。
|
|
3
|
-
|
|
4
|
-
用法:
|
|
5
|
-
check_comments.py <root> [--sdk-paths <json>] [--format text|json]
|
|
6
|
-
|
|
7
|
-
退出码:
|
|
8
|
-
0 = 无违规
|
|
9
|
-
1 = 有违规
|
|
10
|
-
2 = 用法错误
|
|
11
|
-
"""
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import argparse
|
|
15
|
-
import ast
|
|
16
|
-
import json
|
|
17
|
-
import re
|
|
18
|
-
import sys
|
|
19
|
-
from dataclasses import dataclass, asdict
|
|
20
|
-
from pathlib import Path
|
|
21
|
-
from typing import Iterable
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# 敏感模式表,与 references/sensitive-patterns.md 对齐
|
|
25
|
-
PATTERNS: list[tuple[str, re.Pattern[str]]] = [
|
|
26
|
-
("internal-domain", re.compile(r"\b[\w-]+\.(internal|corp|local|intranet)\b", re.I)),
|
|
27
|
-
("private-ip", re.compile(
|
|
28
|
-
r"\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}"
|
|
29
|
-
r"|192\.168\.\d{1,3}\.\d{1,3}"
|
|
30
|
-
r"|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3})\b"
|
|
31
|
-
)),
|
|
32
|
-
("sql-statement", re.compile(
|
|
33
|
-
r"\b(SELECT|UPDATE|DELETE|INSERT)\b[^.\n]{0,80}\b(FROM|WHERE|SET|INTO)\b", re.I
|
|
34
|
-
)),
|
|
35
|
-
("redis-key-pattern", re.compile(r"\b(cache|redis|fmt|lock|queue):[a-z_]+:")),
|
|
36
|
-
("jira-ticket", re.compile(r"\b[A-Z]{2,6}-\d{2,6}\b")),
|
|
37
|
-
("internal-service", re.compile(
|
|
38
|
-
r"\b[a-z_]+_(?:service|server|grpc|rpc)_(?:v\d|prod|stg|stage|dev)\b"
|
|
39
|
-
)),
|
|
40
|
-
("aws-arn", re.compile(r"arn:aws:[a-z0-9-]+:[a-z0-9-]*:\d+:[a-zA-Z0-9-_/:]+")),
|
|
41
|
-
("aws-key", re.compile(r"\bAKIA[0-9A-Z]{16}\b")),
|
|
42
|
-
("cred-keyword", re.compile(
|
|
43
|
-
r"\b(API_KEY|TOKEN|SECRET|PASSWORD|PRIVATE_KEY)\s*[=:]\s*['\"][\w\-./+]{6,}['\"]"
|
|
44
|
-
)),
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
# 弱模式(仅在行内有上下文关键字时命中,降误报)
|
|
48
|
-
PORT_PATTERN = re.compile(r":(\d{4,5})\b")
|
|
49
|
-
PORT_CONTEXT = re.compile(r"\b(connect|server|host|endpoint|address|listen|bind)\b", re.I)
|
|
50
|
-
|
|
51
|
-
EMAIL_PATTERN = re.compile(r"[\w.+-]+@[\w.-]+\.[a-z]{2,}")
|
|
52
|
-
PUBLIC_EMAIL_DOMAINS = {
|
|
53
|
-
"example.com", "example.org", "example.net",
|
|
54
|
-
"gmail.com", "outlook.com", "hotmail.com", "qq.com", "163.com",
|
|
55
|
-
"yourdomain.com", "your-domain.com", "domain.com",
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
# 行内示例 / 占位关键字 → 整行视为示例不告警
|
|
59
|
-
EXAMPLE_HINTS = re.compile(r"\b(example|示例|e\.g\.|placeholder|占位|your[- ]?domain|<.*>)\b", re.I)
|
|
60
|
-
|
|
61
|
-
# 行内豁免
|
|
62
|
-
IGNORE_MARKER = re.compile(r"#\s*sdk-spec:\s*ignore|//\s*sdk-spec:\s*ignore")
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@dataclass
|
|
66
|
-
class Violation:
|
|
67
|
-
file: str
|
|
68
|
-
line: int
|
|
69
|
-
pattern: str
|
|
70
|
-
match: str
|
|
71
|
-
context: str
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def scan_text(file_rel: str, text: str, start_line_offset: int = 0) -> list[Violation]:
|
|
75
|
-
"""扫一段连续文本(docstring 或注释块),返回违规列表。
|
|
76
|
-
|
|
77
|
-
`text` 内部带换行,`start_line_offset` 是该文本第一行在文件中的实际行号。
|
|
78
|
-
"""
|
|
79
|
-
out: list[Violation] = []
|
|
80
|
-
for i, line in enumerate(text.splitlines(), start=start_line_offset):
|
|
81
|
-
if IGNORE_MARKER.search(line):
|
|
82
|
-
continue
|
|
83
|
-
line_stripped = line.strip()
|
|
84
|
-
if not line_stripped:
|
|
85
|
-
continue
|
|
86
|
-
|
|
87
|
-
is_example = bool(EXAMPLE_HINTS.search(line))
|
|
88
|
-
|
|
89
|
-
# 主模式
|
|
90
|
-
for pat_name, pat in PATTERNS:
|
|
91
|
-
for m in pat.finditer(line):
|
|
92
|
-
if is_example and pat_name in {"sql-statement", "internal-domain", "jira-ticket"}:
|
|
93
|
-
continue
|
|
94
|
-
out.append(Violation(
|
|
95
|
-
file=file_rel, line=i,
|
|
96
|
-
pattern=pat_name, match=m.group(0),
|
|
97
|
-
context=line_stripped[:160],
|
|
98
|
-
))
|
|
99
|
-
|
|
100
|
-
# 端口(弱模式)
|
|
101
|
-
if not is_example and PORT_CONTEXT.search(line):
|
|
102
|
-
for m in PORT_PATTERN.finditer(line):
|
|
103
|
-
port = int(m.group(1))
|
|
104
|
-
if port in {8080, 8000, 3000, 5000, 5173, 5432, 6379, 9000}:
|
|
105
|
-
# 常见示例端口,跳过
|
|
106
|
-
continue
|
|
107
|
-
out.append(Violation(
|
|
108
|
-
file=file_rel, line=i,
|
|
109
|
-
pattern="service-port", match=m.group(0),
|
|
110
|
-
context=line_stripped[:160],
|
|
111
|
-
))
|
|
112
|
-
|
|
113
|
-
# 邮箱(弱模式)
|
|
114
|
-
if not is_example:
|
|
115
|
-
for m in EMAIL_PATTERN.finditer(line):
|
|
116
|
-
email = m.group(0)
|
|
117
|
-
domain = email.split("@", 1)[1].lower()
|
|
118
|
-
if domain in PUBLIC_EMAIL_DOMAINS:
|
|
119
|
-
continue
|
|
120
|
-
out.append(Violation(
|
|
121
|
-
file=file_rel, line=i,
|
|
122
|
-
pattern="internal-email", match=email,
|
|
123
|
-
context=line_stripped[:160],
|
|
124
|
-
))
|
|
125
|
-
return out
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def scan_python_file(path: Path, root: Path) -> list[Violation]:
|
|
129
|
-
rel = str(path.relative_to(root))
|
|
130
|
-
try:
|
|
131
|
-
source = path.read_text(encoding="utf-8")
|
|
132
|
-
except (UnicodeDecodeError, OSError):
|
|
133
|
-
return []
|
|
134
|
-
try:
|
|
135
|
-
tree = ast.parse(source)
|
|
136
|
-
except SyntaxError:
|
|
137
|
-
return []
|
|
138
|
-
|
|
139
|
-
out: list[Violation] = []
|
|
140
|
-
# 模块、函数、类、异步函数的 docstring
|
|
141
|
-
for node in ast.walk(tree):
|
|
142
|
-
if not isinstance(node, (ast.Module, ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
143
|
-
continue
|
|
144
|
-
doc = ast.get_docstring(node, clean=False)
|
|
145
|
-
if not doc:
|
|
146
|
-
continue
|
|
147
|
-
# docstring 首行在 node.body[0].lineno(第一条 Expr 是字符串)
|
|
148
|
-
first_stmt = node.body[0] if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) else node.body[0]
|
|
149
|
-
start_line = getattr(first_stmt, "lineno", 1)
|
|
150
|
-
out.extend(scan_text(rel, doc, start_line_offset=start_line))
|
|
151
|
-
return out
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
# 匹配 JS/TS 的 /** ... */ JSDoc 块(不含其他 /* */ 注释)
|
|
155
|
-
JSDOC_BLOCK = re.compile(r"/\*\*(.*?)\*/", re.DOTALL)
|
|
156
|
-
# 行注释 //
|
|
157
|
-
LINE_COMMENT = re.compile(r"^\s*//\s?(.*)$")
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def scan_js_file(path: Path, root: Path) -> list[Violation]:
|
|
161
|
-
rel = str(path.relative_to(root))
|
|
162
|
-
try:
|
|
163
|
-
source = path.read_text(encoding="utf-8")
|
|
164
|
-
except (UnicodeDecodeError, OSError):
|
|
165
|
-
return []
|
|
166
|
-
|
|
167
|
-
out: list[Violation] = []
|
|
168
|
-
|
|
169
|
-
# JSDoc 块
|
|
170
|
-
for m in JSDOC_BLOCK.finditer(source):
|
|
171
|
-
block_text = m.group(1)
|
|
172
|
-
# 起始行号
|
|
173
|
-
start_line = source.count("\n", 0, m.start()) + 1
|
|
174
|
-
# 去掉每行的 leading * 和空白
|
|
175
|
-
cleaned_lines = []
|
|
176
|
-
for line in block_text.splitlines():
|
|
177
|
-
cleaned_lines.append(re.sub(r"^\s*\*\s?", "", line))
|
|
178
|
-
out.extend(scan_text(rel, "\n".join(cleaned_lines), start_line_offset=start_line))
|
|
179
|
-
|
|
180
|
-
# 行内注释(只扫描,因为商业 SDK 内部实现的行注释虽然不打包但开发期能看到)
|
|
181
|
-
# 注:源文件其实是否打包取决于配置,行注释泄露主要看是否打包 src/
|
|
182
|
-
# 跨边界谨慎起见也扫
|
|
183
|
-
for i, line in enumerate(source.splitlines(), start=1):
|
|
184
|
-
lm = LINE_COMMENT.match(line)
|
|
185
|
-
if not lm:
|
|
186
|
-
continue
|
|
187
|
-
comment = lm.group(1)
|
|
188
|
-
out.extend(scan_text(rel, comment, start_line_offset=i))
|
|
189
|
-
|
|
190
|
-
return out
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def iter_target_files(root: Path, sdk_paths: list[str] | None) -> Iterable[Path]:
|
|
194
|
-
"""枚举要扫的文件。sdk_paths 为 None 时全扫;否则只扫指定路径。"""
|
|
195
|
-
excludes = {
|
|
196
|
-
"node_modules", ".venv", "venv", "dist", "build",
|
|
197
|
-
".git", "__pycache__", ".mypy_cache", ".pytest_cache",
|
|
198
|
-
".ruff_cache", ".tox", "egg-info", "site-packages",
|
|
199
|
-
}
|
|
200
|
-
bases = [root / p for p in sdk_paths] if sdk_paths else [root]
|
|
201
|
-
for base in bases:
|
|
202
|
-
if not base.exists():
|
|
203
|
-
continue
|
|
204
|
-
for p in base.rglob("*"):
|
|
205
|
-
if not p.is_file():
|
|
206
|
-
continue
|
|
207
|
-
if any(part in excludes or part.endswith(".egg-info") for part in p.parts):
|
|
208
|
-
continue
|
|
209
|
-
suf = p.suffix.lower()
|
|
210
|
-
if suf in {".py", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"}:
|
|
211
|
-
yield p
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def main() -> int:
|
|
215
|
-
parser = argparse.ArgumentParser(description="Scan SDK comments for sensitive patterns.")
|
|
216
|
-
parser.add_argument("root", help="Project root directory (absolute path)")
|
|
217
|
-
parser.add_argument("--sdk-paths", default=None,
|
|
218
|
-
help='JSON {"python":[...], "js":[...]} relative to root. Omit to scan all.')
|
|
219
|
-
parser.add_argument("--format", choices=["text", "json"], default="text")
|
|
220
|
-
args = parser.parse_args()
|
|
221
|
-
|
|
222
|
-
root = Path(args.root).resolve()
|
|
223
|
-
if not root.is_dir():
|
|
224
|
-
print(f"ERROR: not a directory: {root}", file=sys.stderr)
|
|
225
|
-
return 2
|
|
226
|
-
|
|
227
|
-
target_paths: list[str] | None = None
|
|
228
|
-
if args.sdk_paths:
|
|
229
|
-
try:
|
|
230
|
-
sdk_spec = json.loads(args.sdk_paths)
|
|
231
|
-
except json.JSONDecodeError as e:
|
|
232
|
-
print(f"ERROR: invalid --sdk-paths JSON: {e}", file=sys.stderr)
|
|
233
|
-
return 2
|
|
234
|
-
target_paths = []
|
|
235
|
-
for key in ("python", "js"):
|
|
236
|
-
target_paths.extend(sdk_spec.get(key, []))
|
|
237
|
-
|
|
238
|
-
violations: list[Violation] = []
|
|
239
|
-
for fp in iter_target_files(root, target_paths):
|
|
240
|
-
if fp.suffix == ".py":
|
|
241
|
-
violations.extend(scan_python_file(fp, root))
|
|
242
|
-
else:
|
|
243
|
-
violations.extend(scan_js_file(fp, root))
|
|
244
|
-
|
|
245
|
-
if args.format == "json":
|
|
246
|
-
print(json.dumps([asdict(v) for v in violations], ensure_ascii=False, indent=2))
|
|
247
|
-
else:
|
|
248
|
-
if not violations:
|
|
249
|
-
print("OK: no sensitive patterns found")
|
|
250
|
-
else:
|
|
251
|
-
print(f"FAIL: {len(violations)} violation(s) found:\n")
|
|
252
|
-
for v in violations:
|
|
253
|
-
print(f" {v.file}:{v.line} [{v.pattern}] match={v.match!r}")
|
|
254
|
-
print(f" > {v.context}")
|
|
255
|
-
|
|
256
|
-
return 1 if violations else 0
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if __name__ == "__main__":
|
|
260
|
-
sys.exit(main())
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# JS/TS SDK 发布前文件清单审计(npm pack --dry-run)
|
|
3
|
-
# 用法: check_js_publish.sh <js_sdk_root> --edition commercial|opensource
|
|
4
|
-
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
if [[ $# -lt 3 ]]; then
|
|
8
|
-
echo "Usage: $0 <js_sdk_root> --edition commercial|opensource" >&2
|
|
9
|
-
exit 2
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
PKG_ROOT="$1"
|
|
13
|
-
shift
|
|
14
|
-
|
|
15
|
-
EDITION=""
|
|
16
|
-
while [[ $# -gt 0 ]]; do
|
|
17
|
-
case "$1" in
|
|
18
|
-
--edition) EDITION="$2"; shift 2 ;;
|
|
19
|
-
*) echo "ERROR: unknown arg: $1" >&2; exit 2 ;;
|
|
20
|
-
esac
|
|
21
|
-
done
|
|
22
|
-
|
|
23
|
-
if [[ "$EDITION" != "commercial" && "$EDITION" != "opensource" ]]; then
|
|
24
|
-
echo "ERROR: --edition must be 'commercial' or 'opensource'" >&2
|
|
25
|
-
exit 2
|
|
26
|
-
fi
|
|
27
|
-
|
|
28
|
-
if [[ ! -d "$PKG_ROOT" ]]; then
|
|
29
|
-
echo "ERROR: not a directory: $PKG_ROOT" >&2
|
|
30
|
-
exit 2
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
if ! command -v npm >/dev/null 2>&1; then
|
|
34
|
-
echo "ERROR: npm not found in PATH" >&2
|
|
35
|
-
exit 2
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
echo "[1/2] Running npm pack --dry-run in $PKG_ROOT ..."
|
|
39
|
-
|
|
40
|
-
# npm pack --dry-run --json 输出包内文件列表
|
|
41
|
-
# 注:某些 npm 版本 --json 输出到 stderr,--silent 抑制非 JSON 输出
|
|
42
|
-
PACK_OUTPUT=$(cd "$PKG_ROOT" && npm pack --dry-run --json 2>/dev/null) || {
|
|
43
|
-
echo "ERROR: npm pack --dry-run failed; run 'npm install' first?" >&2
|
|
44
|
-
exit 2
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
# 提取文件路径列表
|
|
48
|
-
FILES=$(echo "$PACK_OUTPUT" | jq -r '.[0].files[].path' 2>/dev/null) || {
|
|
49
|
-
echo "ERROR: failed to parse npm pack output:" >&2
|
|
50
|
-
echo "$PACK_OUTPUT" | head -20 >&2
|
|
51
|
-
exit 2
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
echo "[2/2] Audit result (edition=$EDITION):"
|
|
55
|
-
echo "----------------------------------------"
|
|
56
|
-
|
|
57
|
-
# 致命级(任何版本都禁)
|
|
58
|
-
CRITICAL_REGEX='(^|/)(\.env(\..+)?|\.npmrc|\.pypirc|\.git-credentials|id_rsa|id_ed25519|secrets\.json|credentials\.json)$|\.(pem|key|p12)$|(^|/)\.aws/credentials$'
|
|
59
|
-
|
|
60
|
-
# 开源版黑名单
|
|
61
|
-
OPENSOURCE_DENY_REGEX='(^|/)(src|tests|__tests__|\.git|\.github|\.gitlab-ci\.yml|\.travis\.yml|\.circleci|\.vscode|\.idea|node_modules|\.husky)/|(^|/)(tsconfig.*\.json|vite\.config\.|vitest\.config\.|tsup\.config\.|rollup\.config\.|webpack\.config\.|jest\.config\.|\.eslintrc.*|\.prettierrc.*|eslint\.config\.|prettier\.config\.|commitlint\.config\.|\.gitignore|\.gitattributes|\.DS_Store|Thumbs\.db|pnpm-lock\.yaml|package-lock\.json|yarn\.lock)|\.(map|tsbuildinfo|swp|swo)$|\.(test|spec)\.(ts|tsx|js|jsx)$'
|
|
62
|
-
|
|
63
|
-
# 商业版白名单(其他视为违规)
|
|
64
|
-
COMMERCIAL_ALLOW_REGEX='^(dist/|bin/|package\.json$|README(\..+)?$|LICEN[SC]E(\..+)?$|CHANGELOG(\..+)?$)'
|
|
65
|
-
|
|
66
|
-
violations=()
|
|
67
|
-
critical=()
|
|
68
|
-
|
|
69
|
-
while IFS= read -r f; do
|
|
70
|
-
[[ -z "$f" ]] && continue
|
|
71
|
-
|
|
72
|
-
if [[ "$f" =~ $CRITICAL_REGEX ]]; then
|
|
73
|
-
critical+=("$f")
|
|
74
|
-
continue
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
if [[ "$EDITION" == "opensource" ]]; then
|
|
78
|
-
if [[ "$f" =~ $OPENSOURCE_DENY_REGEX ]]; then
|
|
79
|
-
violations+=("$f [denied by opensource rules]")
|
|
80
|
-
fi
|
|
81
|
-
else
|
|
82
|
-
if [[ ! "$f" =~ $COMMERCIAL_ALLOW_REGEX ]]; then
|
|
83
|
-
violations+=("$f [not in commercial whitelist]")
|
|
84
|
-
fi
|
|
85
|
-
fi
|
|
86
|
-
done <<< "$FILES"
|
|
87
|
-
|
|
88
|
-
if [[ ${#critical[@]} -gt 0 ]]; then
|
|
89
|
-
echo "[CRITICAL] secret-like files in package — DO NOT PUBLISH:"
|
|
90
|
-
for f in "${critical[@]}"; do
|
|
91
|
-
echo " $f"
|
|
92
|
-
done
|
|
93
|
-
echo ""
|
|
94
|
-
fi
|
|
95
|
-
|
|
96
|
-
if [[ ${#violations[@]} -gt 0 ]]; then
|
|
97
|
-
echo "[VIOLATION] ${#violations[@]} file(s) violate $EDITION rules:"
|
|
98
|
-
for v in "${violations[@]}"; do
|
|
99
|
-
echo " $v"
|
|
100
|
-
done
|
|
101
|
-
echo ""
|
|
102
|
-
exit 1
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
if [[ ${#critical[@]} -gt 0 ]]; then
|
|
106
|
-
exit 1
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
count=$(echo "$FILES" | grep -c .)
|
|
110
|
-
echo "OK: package passes $EDITION audit ($count file(s))"
|