@cnbcool/cnb-api-generate 2.14.0 → 2.14.1

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.
@@ -1,5 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import { resolveToken } from '../utils/resolve-token';
3
+ import { getGitRemoteHosts } from '../utils/resolve-platform-host';
3
4
 
4
5
  /**
5
6
  * 用作 git credential helper。
@@ -11,7 +12,7 @@ import { resolveToken } from '../utils/resolve-token';
11
12
  *
12
13
  * git 在执行需要凭据的操作时,会以 `get`、`store`、`erase` 之一作为 action 调用本命令,
13
14
  * 并通过 stdin 传入若干 `key=value` 形式的字段(protocol/host/path 等)。
14
- * 我们仅处理 `get`:根据 host 白名单校验后,从后端获取凭据,按 git-credential
15
+ * 我们仅处理 `get`:校验 host 属于当前仓库 git remote 的域名后,从后端获取凭据,按 git-credential
15
16
  * 协议把 `username=...` / `password=...` 写到 stdout;`store` / `erase` 直接忽略。
16
17
  */
17
18
 
@@ -27,8 +28,6 @@ interface Credential {
27
28
  password: string;
28
29
  }
29
30
 
30
- const ALLOWED_HOSTS = ['cnb.cool', 'cnb.woa.com'];
31
-
32
31
  /** git-credential get 等待 stdin 的最长时间,避免 git 无限挂起。 */
33
32
  const GET_INPUT_TIMEOUT_MS = 8000;
34
33
 
@@ -73,7 +72,7 @@ export function registerGitCredentialCommand(program: Command): void {
73
72
  }
74
73
 
75
74
  /**
76
- * 校验白名单并向后端获取凭据,按照 git-credential 协议输出 username/password。
75
+ * 校验 remote 域名并向后端获取凭据,按照 git-credential 协议输出 username/password。
77
76
  */
78
77
  async function auth(data: GitCredentialInput): Promise<void> {
79
78
  const { host } = data;
@@ -81,8 +80,11 @@ async function auth(data: GitCredentialInput): Promise<void> {
81
80
  `[git-credential]: for ${data.protocol}://${data.host}/${data.path ?? ''}`,
82
81
  );
83
82
 
84
- // 必须校验域名,否则传入第三方域名就会被盗密码
85
- if (!host || !ALLOWED_HOSTS.includes(host)) {
83
+ // 只向当前仓库 git remote 的域名提供凭据;remote 由用户配置(含子域名、api 子域),
84
+ // git-credential 调用链天然与仓库绑定,非本仓库 remote 的域名一律拒绝,
85
+ // 避免第三方域名盗取密码
86
+ const remoteHosts = getGitRemoteHosts();
87
+ if (!host || remoteHosts.length === 0 || !remoteHosts.includes(host)) {
86
88
  throw new Error(`unknown host: ${host}`);
87
89
  }
88
90
 
@@ -9,19 +9,20 @@ import {
9
9
  type TokenStore,
10
10
  } from '../utils/device-auth';
11
11
  import { tryOpenBrowser } from '../utils/open-browser';
12
+ import { resolvePlatformUrl } from '../utils/resolve-platform-host';
12
13
 
13
14
  export function registerLoginCommand(program: Command): void {
14
15
  program
15
16
  .command('login')
16
17
  .description('通过 OAuth2 设备授权流登录 CNB,获取并保存 access_token')
17
18
  .option('--client-id <string>', 'OAuth2 client_id', process.env.OAUTH2_CLIENT_ID || 'cnb_cli')
18
- .option('--woa', '使用内网环境 (https://cnb.woa.com)', false)
19
19
  .option('--debug', '打印调试信息', false)
20
20
  .helpOption('-h, --help', '显示帮助文档')
21
21
  .action(async (opts) => {
22
22
  const cfg: LoginConfig = {
23
23
  clientID: opts.clientId,
24
- platformURL: opts.woa ? 'https://cnb.woa.com' : 'https://cnb.cool',
24
+ // git remote -v 动态识别平台域名,兼容不同部署(公开 / 内网 / 自定义域名)
25
+ platformURL: resolvePlatformUrl(),
25
26
  debug: opts.debug,
26
27
  };
27
28
 
@@ -0,0 +1,64 @@
1
+ import { execSync } from 'node:child_process';
2
+
3
+ /**
4
+ * 从当前仓库的 `git remote -v` 动态识别平台域名。
5
+ *
6
+ * 统一以 git remote 解析为准,不再维护静态域名白名单,
7
+ * 天然兼容不同平台部署(公开 / 内网 / 自定义域名)。
8
+ */
9
+
10
+ /**
11
+ * 执行 `git remote -v` 并解析出所有 remote 的 host(去重)。
12
+ * 仅在当前目录是 git 仓库时返回非空数组;解析失败或非 git 仓库时返回空数组。
13
+ * @returns 去重后的 host 列表,如 ['cnb.cool']
14
+ */
15
+ export function getGitRemoteHosts(): string[] {
16
+ try {
17
+ const out = execSync('git remote -v', {
18
+ encoding: 'utf8',
19
+ stdio: ['ignore', 'pipe', 'ignore'],
20
+ });
21
+ const hosts = new Set<string>();
22
+ for (const line of out.split('\n')) {
23
+ // 每行形如:origin\thttps://cnb.cool/cnb/skills/cnb-skill.git (fetch)
24
+ const url = line.split(/\s+/)[1];
25
+ if (!url) continue;
26
+ const host = parseUrlHost(url);
27
+ if (host) hosts.add(host);
28
+ }
29
+ return [...hosts];
30
+ } catch {
31
+ // 非 git 仓库或 git 不可用等场景,静默返回空数组
32
+ return [];
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 从 remote URL 中解析出 host。
38
+ * 兼容 https://、http://、ssh:// 以及 scp-like 形式(git@host:path)。
39
+ * @param url
40
+ * @returns host,解析失败返回 null
41
+ */
42
+ function parseUrlHost(url: string): string | null {
43
+ try {
44
+ // scp-like: git@github.com:org/repo.git
45
+ if (/^[^@/]+@[^:/]+:/.test(url) && !/^[a-z]+:\/\//i.test(url)) {
46
+ const host = url.slice(url.indexOf('@') + 1, url.indexOf(':'));
47
+ return host || null;
48
+ }
49
+ return new URL(url).hostname || null;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 解析当前平台的 base URL(形如 https://cnb.cool)。
57
+ * 优先取 git remote 中的 host 拼成 https://{host};
58
+ * 无 remote 或解析失败时兜底返回 https://cnb.cool。
59
+ * @returns 平台 base URL
60
+ */
61
+ export function resolvePlatformUrl(): string {
62
+ const host = getGitRemoteHosts()[0];
63
+ return host ? `https://${host}` : 'https://cnb.cool';
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cnbcool/cnb-api-generate",
3
- "version": "2.14.0",
3
+ "version": "2.14.1",
4
4
  "main": "./built/index.js",
5
5
  "module": "./src/index.ts",
6
6
  "types": "./src/index.ts",