@oj-module/cli 0.2.30 → 0.2.42
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/devkit/manual.md +5 -5
- package/devkit/module-dev-and-cdn-playground.md +1 -1
- package/package.json +2 -2
- package/shell-dist/assets/runtime.js +2 -2
- package/shell-dist/index.html +2 -2
- package/shell-dist/versions.json +3 -3
- package/src/__tests__/cli.test.ts +23 -14
- package/src/__tests__/list.test.ts +176 -0
- package/src/__tests__/registry-cli.test.ts +522 -0
- package/src/__tests__/registry-export.test.ts +319 -0
- package/src/__tests__/registry-import.test.ts +336 -0
- package/src/__tests__/registry-sign.test.ts +352 -0
- package/src/cli-output.ts +81 -0
- package/src/cli.ts +298 -39
- package/src/config-cmd.ts +100 -0
- package/src/contract/__tests__/handler-scan.test.ts +74 -2
- package/src/contract/handler-scan.ts +27 -2
- package/src/enable.ts +1 -1
- package/src/exit-codes.ts +33 -0
- package/src/index.ts +5 -9
- package/src/install.ts +120 -38
- package/src/lifecycle/__tests__/authoring.test.ts +6 -5
- package/src/lifecycle/__tests__/fixtures/sig/demo-solution-0.2.0.tgz.bad.sig +3 -2
- package/src/lifecycle/__tests__/fixtures/sig/demo-solution-0.2.0.tgz.sig +3 -2
- package/src/lifecycle/__tests__/fixtures/sig/demo-solution-0.2.0.tgz.unknown.sig +3 -2
- package/src/lifecycle/__tests__/fixtures/sig/gen-signed-fixtures.d.mts +5 -4
- package/src/lifecycle/__tests__/fixtures/sig/gen-signed-fixtures.mjs +9 -6
- package/src/lifecycle/__tests__/helpers.ts +10 -6
- package/src/lifecycle/__tests__/install.test.ts +72 -15
- package/src/lifecycle/__tests__/manifest-contract.test.ts +40 -1
- package/src/lifecycle/__tests__/publish.test.ts +174 -33
- package/src/lifecycle/__tests__/sig-fixtures.test.ts +9 -6
- package/src/lifecycle/__tests__/sign.test.ts +9 -8
- package/src/lifecycle/__tests__/smoke.test.ts +1 -1
- package/src/lifecycle/__tests__/uninstall.test.ts +4 -4
- package/src/lifecycle/sign.ts +8 -7
- package/src/list.ts +146 -0
- package/src/manifest-contract.ts +11 -5
- package/src/market/__tests__/bootstrap.test.ts +4 -1
- package/src/market/__tests__/build-index.test.ts +529 -42
- package/src/market/__tests__/fixture.test.ts +17 -1
- package/src/market/__tests__/publish.test.ts +8 -7
- package/src/market/__tests__/setup.test.ts +4 -4
- package/src/market/__tests__/sharding.test.ts +90 -0
- package/src/market/__tests__/verify.test.ts +205 -12
- package/src/market/__tests__/yank-ledger.test.ts +60 -0
- package/src/market/bootstrap.ts +8 -3
- package/src/market/build-index.ts +407 -40
- package/src/market/cdn-layout.ts +3 -2
- package/src/market/command.ts +40 -6
- package/src/market/failure-playbook.ts +1 -1
- package/src/market/fixture.ts +20 -3
- package/src/market/index-builder.ts +120 -47
- package/src/market/publish.ts +3 -1
- package/src/market/setup.ts +3 -3
- package/src/market/sharding.ts +94 -0
- package/src/market/trial.ts +1 -1
- package/src/market/verify.ts +213 -50
- package/src/market/walkthrough.ts +7 -6
- package/src/market/yank-ledger.ts +91 -0
- package/src/publish.ts +250 -38
- package/src/registry-export.ts +405 -0
- package/src/registry-import.ts +410 -0
- package/src/registry-sidecar.ts +84 -0
- package/src/registry-sign.ts +322 -0
- package/src/registry-source.ts +192 -0
- package/src/sign.ts +13 -10
- package/src/site.ts +4 -4
- package/src/trust.ts +1 -1
- package/src/uninstall.ts +14 -2
- package/src/usage.ts +130 -25
- package/src/view.ts +94 -0
- package/templates/cdn/README.md +35 -5
- package/templates/config.yaml +10 -0
- package/templates/init.include.yaml +1 -1
- package/templates/src/_platform/api/_lifecycle/atomic.ts +3 -0
- package/templates/src/_platform/api/_lifecycle/installer.ts +44 -7
- package/templates/src/_platform/api/_lifecycle/limits.ts +21 -0
- package/templates/src/_platform/api/_lifecycle/source-seq.ts +88 -0
- package/templates/src/_platform/api/_lifecycle/steps/download.ts +15 -0
- package/templates/src/_platform/api/_lifecycle/steps/extract.ts +61 -2
- package/templates/src/_platform/api/_lifecycle/steps/preflight.ts +55 -6
- package/templates/src/_platform/api/_lifecycle/steps/register.ts +2 -1
- package/templates/src/_platform/api/_lifecycle/steps/verify.ts +22 -8
- package/templates/src/_platform/api/_lifecycle/tgz-guard.ts +96 -0
- package/templates/src/_platform/api/_lifecycle/types.ts +6 -3
- package/templates/src/_platform/api/_lifecycle/verify-checks.ts +8 -0
- package/templates/src/_platform/api/_shared/service/domain/solution-perms.ts +9 -1
- package/templates/src/_platform/api/_shared/site-facade.ts +62 -11
- package/templates/src/_platform/api/manifest.yaml +1 -0
- package/templates/src/_platform/api/market/api.ts +362 -99
- package/templates/src/_platform/api/market/index-source.ts +112 -0
- package/templates/src/_platform/api/migrations/0014__reg_source_seq.mysql.sql +9 -0
- package/templates/src/_platform/api/migrations/0014__reg_source_seq.pg.sql +7 -0
- package/templates/src/_platform/api/migrations/0014__reg_source_seq.sql +8 -0
- package/templates/src/_platform/api/schema.yaml +8 -0
- package/templates/src/_platform/api/solutions/api.ts +8 -3
- package/templates/src/oj-market/web/__tests__/CardGrid.test.tsx +13 -0
- package/templates/src/oj-market/web/__tests__/InstallDialog.test.tsx +38 -0
- package/templates/src/oj-market/web/components/CardItem.tsx +10 -0
- package/templates/src/oj-market/web/components/InstallDialog.tsx +18 -0
- package/templates/src/oj-market/web/locales/en-US.json +1 -0
- package/templates/src/oj-market/web/locales/zh-CN.json +1 -0
- package/templates/src/oj-market/web/types.ts +3 -1
- package/templates/tests/lifecycle/installer.spec.ts +24 -0
- package/templates/tests/lifecycle/market-api.spec.ts +641 -85
- package/templates/tests/lifecycle/preflight.spec.ts +52 -0
- package/templates/tests/lifecycle/sig-fixtures.spec.ts +7 -4
- package/templates/tests/lifecycle/source-seq.spec.ts +55 -0
- package/templates/tests/lifecycle/steps-fs.spec.ts +166 -7
- package/templates/tests/lifecycle/tgz-guard.spec.ts +133 -1
- package/templates/tests/lifecycle/verify-checks.spec.ts +27 -13
- package/templates/tests/lifecycle/verify-report.spec.ts +3 -1
- package/templates/tests/rbac-solution-perms.spec.ts +41 -0
- package/vendor/host-versions.json +3 -3
|
@@ -3,32 +3,83 @@
|
|
|
3
3
|
// 单一真源:market/api.ts re-export 保持既有引用面;深检/方案域直接 import 本模块。
|
|
4
4
|
|
|
5
5
|
import type { FetchFace } from "../_lifecycle/steps/download";
|
|
6
|
+
import { fsFace } from "../_lifecycle/atomic";
|
|
6
7
|
|
|
7
8
|
/** 带缓存指令的拉取面(FetchFace 的超集:oj 全局 fetch/测试 stub 天然满足,少参函数可赋多参形) */
|
|
8
9
|
export type FetchInitFace = (url: string, init?: { headers?: Record<string, string>, redirect?: string }) => Promise<{
|
|
9
10
|
ok: boolean
|
|
10
11
|
status: number
|
|
11
12
|
arrayBuffer: () => Promise<ArrayBuffer>
|
|
13
|
+
/**
|
|
14
|
+
* 响应头读取面(REG-CS5 读面缓存):读 ETag 发 If-None-Match → 304 不重传正文。
|
|
15
|
+
* 可选字段:真实 fetch 的 Response.headers 天然满足;不暴露响应头的测试 stub 省略即按
|
|
16
|
+
* 「无 ETag 语义」退化为每次全量拉取——语义正确,只是少了条件请求的省字节。
|
|
17
|
+
*/
|
|
18
|
+
headers?: { get: (name: string) => string | null }
|
|
12
19
|
}>;
|
|
13
20
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
/**
|
|
22
|
+
* 站点 canonical origin(REG-CS6 Host 头 SSRF 修复):**绝不**读请求头 host / x-forwarded-proto——
|
|
23
|
+
* 伪造 Host(如 169.254.169.254 云元数据)若被用来补全相对 URL,出站请求会被牵引到攻击者指定的
|
|
24
|
+
* 源。一律走站点配置 `vars.canonical_origin`(模板 config.yaml 随发,部署侧填真实对外地址)。
|
|
25
|
+
*
|
|
26
|
+
* - 给定合法 origin(http(s)://host,无路径)→ 返回 {proto, host};
|
|
27
|
+
* - 缺失 / 非法 / 空 → 返回 null(不补全、不回落请求头;相对 URL 原样透传,由下层 fetch 判不可用)。
|
|
28
|
+
* 这是 fail-closed:没有可信源就不替请求「猜」一个,避免把 Host 头当可信输入。
|
|
29
|
+
*/
|
|
30
|
+
export function siteOrigin(canonicalOrigin?: string | null): { proto: string, host: string } | null {
|
|
31
|
+
if (!canonicalOrigin || !canonicalOrigin.trim()) {
|
|
19
32
|
return null;
|
|
20
33
|
}
|
|
21
|
-
const
|
|
22
|
-
|
|
34
|
+
const m = /^(https?):\/\/([^/?#]+)$/i.exec(canonicalOrigin.trim());
|
|
35
|
+
if (m === null) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return { proto: m[1]!.toLowerCase(), host: m[2]! };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 读站点 canonical origin(config.yaml `vars.canonical_origin`;缺文件/缺字段/解析失败 → null)。
|
|
43
|
+
* 隔离运行时无 node:fs/yaml:统一走 fsFace() + globalThis.yaml(与 _platform 其余读盘一致)。
|
|
44
|
+
* 异步:config 读取在隔离环境只有异步面,故 caller(marketEnv/solutionsEnv 是同步的)改为惰性、请求内解析。
|
|
45
|
+
* 进程级缓存:canonical_origin 是部署期安全设置,不会随请求热更,按 configPath 缓存避免每次请求重复读盘。
|
|
46
|
+
*/
|
|
47
|
+
const canonicalOriginCache = new Map<string, Promise<string | null>>();
|
|
48
|
+
|
|
49
|
+
export function readCanonicalOrigin(configPath?: string): Promise<string | null> {
|
|
50
|
+
if (!configPath) {
|
|
51
|
+
return Promise.resolve(null);
|
|
52
|
+
}
|
|
53
|
+
const cached = canonicalOriginCache.get(configPath);
|
|
54
|
+
if (cached !== undefined) {
|
|
55
|
+
return cached;
|
|
56
|
+
}
|
|
57
|
+
const p = (async () => {
|
|
58
|
+
try {
|
|
59
|
+
const bytes = await fsFace().readFile(configPath);
|
|
60
|
+
// yaml.parse 是异步面(oj 隔离全局,全仓消费方同一口径)——必须 await:
|
|
61
|
+
// 漏 await 时 doc 是 Promise,vars 恒 undefined,config 里的 canonical_origin 永远读不到。
|
|
62
|
+
const doc = await (globalThis as { yaml?: { parse: (t: string) => Promise<unknown> } }).yaml?.parse(new TextDecoder().decode(bytes)) as { vars?: { canonical_origin?: unknown } } | undefined;
|
|
63
|
+
const v = doc?.vars?.canonical_origin;
|
|
64
|
+
return typeof v === "string" && v.trim() !== "" ? v.trim() : null;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
canonicalOriginCache.set(configPath, p);
|
|
71
|
+
return p;
|
|
23
72
|
}
|
|
24
73
|
|
|
25
|
-
/** 站点源补全(深检探针复用 siteOrigin;相对 URL → 绝对同源 URL
|
|
26
|
-
export function withSiteOrigin(fetch: FetchInitFace): FetchFace {
|
|
27
|
-
const o = siteOrigin();
|
|
74
|
+
/** 站点源补全(深检探针复用 siteOrigin;相对 URL → 绝对同源 URL;无可信源则不补全) */
|
|
75
|
+
export function withSiteOrigin(fetch: FetchInitFace, canonicalOrigin?: string | null): FetchFace {
|
|
76
|
+
const o = siteOrigin(canonicalOrigin);
|
|
28
77
|
if (o === null) {
|
|
29
78
|
return fetch as FetchFace;
|
|
30
79
|
}
|
|
31
|
-
|
|
80
|
+
// init(如 REG-CS5 条件请求的 if-none-match)必须透传——否则按源补全会静默丢掉调用方的请求头。
|
|
81
|
+
// init 形参可选,故仍可赋给单参的 FetchFace。
|
|
82
|
+
return (url: string, init?: { headers?: Record<string, string>, redirect?: string }) => fetch(/^https?:\/\//i.test(url) ? url : `${o.proto}://${o.host}${url}`, init) as ReturnType<FetchFace>;
|
|
32
83
|
}
|
|
33
84
|
|
|
34
85
|
/** 站点迁移账本 head(oj journal 真源 `_oj_migrations`;表缺失/查询失败 → null = 跳过禁降版判定,不猜) */
|