huaweicloud-devkit 1.1.2-next.0 → 1.1.2-next.2
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/.agents/plugins/marketplace.json +18 -18
- package/README.md +182 -38
- package/README.zh-CN.md +180 -36
- package/bin/dsh-postinstall.cjs +77 -0
- package/bin/setup.cjs +7 -4
- package/cordis.patch.yml +13 -0
- package/integrations/atomcode/hooks/huaweicloud-telemetry.js +181 -0
- package/integrations/atomcode/hooks.json +9 -0
- package/integrations/dsh/hook-plugin.mjs +148 -0
- package/integrations/hermes/hooks/huaweicloud-telemetry.py +238 -0
- package/integrations/hermes/manifest.yaml +43 -0
- package/integrations/opencode/hooks/skill-tracker.js +115 -0
- package/integrations/opencode/opencode.json +1 -4
- package/integrations/workbuddy/hooks/telemetry-tracker.py +226 -0
- package/package.json +74 -43
- package/plugins/huaweicloud-core/.claude-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.codex-plugin/plugin.json +23 -33
- package/plugins/huaweicloud-core/.cursor-plugin/plugin.json +4 -17
- package/plugins/huaweicloud-core/.hermes-plugin/plugin.json +30 -0
- package/plugins/huaweicloud-core/.mcp.json +2 -5
- package/plugins/huaweicloud-core/.workbuddy-plugin/plugin.json +4 -18
- package/plugins/huaweicloud-core/assets/icon.png +0 -0
- package/plugins/huaweicloud-core/assets/logo-dark.png +0 -0
- package/plugins/huaweicloud-core/assets/logo.png +0 -0
- package/plugins/huaweicloud-core/hooks/huaweicloud-safety.py +104 -28
- package/plugins/huaweicloud-core/openclaw.plugin.json +20 -0
- package/plugins/huaweicloud-core/safety/policy.json +75 -99
- package/plugins/huaweicloud-core/safety/rules/cloud-risk-rules.json +196 -35
- package/plugins/huaweicloud-core/skills/huawei-apig/SKILL.md +38 -34
- package/plugins/huaweicloud-core/skills/huawei-billing/SKILL.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-cbr/SKILL.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-cce/SKILL.md +38 -32
- package/plugins/huaweicloud-core/skills/huawei-cloud-eye/SKILL.md +30 -30
- package/plugins/huaweicloud-core/skills/huawei-cts/SKILL.md +20 -20
- package/plugins/huaweicloud-core/skills/huawei-dds-dcs/SKILL.md +31 -31
- package/plugins/huaweicloud-core/skills/huawei-deployment/SKILL.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-dew/SKILL.md +40 -31
- package/plugins/huaweicloud-core/skills/huawei-dew/references/csms-usage.md +8 -1
- package/plugins/huaweicloud-core/skills/huawei-dew/references/kms-usage.md +6 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/SKILL.md +45 -37
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/create-instance.md +57 -6
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/flavors.md +11 -9
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/hc-activity.md +27 -0
- package/plugins/huaweicloud-core/skills/huawei-ecs/references/troubleshooting.md +13 -13
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/SKILL.md +44 -39
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/apig-event-format.md +12 -10
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/create-function.md +21 -20
- package/plugins/huaweicloud-core/skills/huawei-functiongraph/references/triggers.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-gaussdb/SKILL.md +20 -20
- package/plugins/huaweicloud-core/skills/huawei-getting-started/SKILL.md +27 -23
- package/plugins/huaweicloud-core/skills/huawei-iac/SKILL.md +93 -0
- package/plugins/huaweicloud-core/skills/huawei-iac/references/architectures.md +83 -0
- package/plugins/huaweicloud-core/skills/huawei-iac/references/resource-catalog.md +107 -0
- package/plugins/huaweicloud-core/skills/huawei-iac/references/state-file.md +104 -0
- package/plugins/huaweicloud-core/skills/huawei-iam/SKILL.md +30 -30
- package/plugins/huaweicloud-core/skills/huawei-iam/references/policy-examples.md +65 -39
- package/plugins/huaweicloud-core/skills/huawei-iam/references/quick-ref.md +9 -9
- package/plugins/huaweicloud-core/skills/huawei-modelarts/SKILL.md +33 -25
- package/plugins/huaweicloud-core/skills/huawei-obs/SKILL.md +46 -45
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-create.md +19 -19
- package/plugins/huaweicloud-core/skills/huawei-obs/references/bucket-lifecycle.md +12 -7
- package/plugins/huaweicloud-core/skills/huawei-obs/references/common-pitfalls.md +13 -12
- package/plugins/huaweicloud-core/skills/huawei-obs/references/single-file-share.md +4 -4
- package/plugins/huaweicloud-core/skills/huawei-rds/SKILL.md +71 -61
- package/plugins/huaweicloud-core/skills/huawei-sandbox/SKILL.md +776 -44
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/framework-commands.md +84 -0
- package/plugins/huaweicloud-core/skills/huawei-sandbox/references/nginx-templates.md +100 -0
- package/plugins/huaweicloud-core/skills/huawei-smn-dms/SKILL.md +36 -29
- package/plugins/huaweicloud-core/skills/huawei-voucher/SKILL.md +57 -0
- package/plugins/huaweicloud-core/skills/huawei-vpc/SKILL.md +39 -39
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/network.md +22 -15
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/security-group.md +20 -19
- package/plugins/huaweicloud-core/skills/huawei-vpc/references/subnet.md +15 -15
- package/plugins/huaweicloud-core/skills/huawei-waf-aad/SKILL.md +23 -20
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/SKILL.md +13 -13
- package/plugins/huaweicloud-core/skills/huaweicloud-api-and-sdk/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/SKILL.md +21 -2
- package/plugins/huaweicloud-core/skills/huaweicloud-capability-discovery/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/SKILL.md +30 -4
- package/plugins/huaweicloud-core/skills/huaweicloud-cli-and-auth/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-core/SKILL.md +68 -54
- package/plugins/huaweicloud-core/skills/huaweicloud-core/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/report-issue.md +6 -6
- package/plugins/huaweicloud-core/skills/huaweicloud-core/references/select.md +15 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/SKILL.md +0 -1
- package/plugins/huaweicloud-core/skills/huaweicloud-safety/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/SKILL.md +36 -34
- package/plugins/huaweicloud-core/skills/huaweicloud-troubleshooting/agents/openai.yaml +3 -3
- package/plugins/huaweicloud-core/src/auth/agent-registration.mjs +142 -5
- package/plugins/huaweicloud-core/src/auth/credentials.mjs +217 -5
- package/plugins/huaweicloud-core/src/auth/reconcile.mjs +174 -0
- package/plugins/huaweicloud-core/src/auth/service.mjs +57 -2
- package/plugins/huaweicloud-core/src/data/icons-manifest.v1.json +3517 -0
- package/plugins/huaweicloud-core/src/detect-framework.mjs +366 -0
- package/plugins/huaweicloud-core/src/hcloud-cli.mjs +203 -16
- package/plugins/huaweicloud-core/src/icon-library.mjs +176 -0
- package/plugins/huaweicloud-core/src/mcp-protocol.mjs +88 -0
- package/plugins/huaweicloud-core/src/mcp-server-remote.mjs +107 -0
- package/plugins/huaweicloud-core/src/mcp-server.mjs +154 -99
- package/plugins/huaweicloud-core/src/proxy/proxy-agent.mjs +20 -102
- package/plugins/huaweicloud-core/src/proxy/proxy-config.mjs +11 -4
- package/plugins/huaweicloud-core/src/risk-rule-engine.mjs +4 -1
- package/plugins/huaweicloud-core/src/safety-policy.mjs +123 -68
- package/plugins/huaweicloud-core/src/sandbox/hdkitservice-api.mjs +48 -12
- package/plugins/huaweicloud-core/src/sandbox/hwlink-api.mjs +25 -26
- package/plugins/huaweicloud-core/src/sandbox/sandbox-file-server.py +45 -0
- package/plugins/huaweicloud-core/src/sandbox/session-manager.mjs +943 -10
- package/plugins/huaweicloud-core/src/sandbox/uninstall-cleanup.mjs +50 -0
- package/plugins/huaweicloud-core/src/search-market.mjs +49 -14
- package/plugins/huaweicloud-core/src/setup-cli.mjs +3091 -498
- package/plugins/huaweicloud-core/src/telemetry/agent-detect.mjs +40 -0
- package/plugins/huaweicloud-core/src/telemetry/agent-registry.mjs +328 -0
- package/plugins/huaweicloud-core/src/telemetry/telemetry.mjs +378 -0
- package/plugins/huaweicloud-core/src/tools.mjs +1272 -146
- package/plugins/huaweicloud-core/src/update-check.mjs +383 -0
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-exec-client.js +72 -38
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-fair-queue.js +10 -5
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-multiplexer.js +13 -16
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-packet.js +12 -16
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-terminal-channel.js +46 -31
- package/plugins/huaweicloud-core/src/ws-exec/hwlink-tunnel-channel.mjs +268 -0
- package/plugins/huaweicloud-core/src/ws-exec/index.js +3 -0
- package/plugins/huaweicloud-core/src/ws-exec/ws-exec-client.js +40 -13
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/SKILL.md +0 -190
- package/plugins/huaweicloud-core/skills/huawei-cloud-find-skills/scripts/search-skills.mjs +0 -124
|
@@ -3,6 +3,15 @@ import { getProxyUrlForTarget } from './proxy-config.mjs';
|
|
|
3
3
|
let cachedDispatcher = undefined;
|
|
4
4
|
let cachedDispatcherProxyUrl = null;
|
|
5
5
|
|
|
6
|
+
async function importUndici() {
|
|
7
|
+
try {
|
|
8
|
+
/* eslint-disable-next-line n/no-missing-import */
|
|
9
|
+
return await import('node:undici');
|
|
10
|
+
} catch {
|
|
11
|
+
return await import('undici');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
export async function getProxyDispatcher(targetUrl) {
|
|
7
16
|
const proxyUrl = getProxyUrlForTarget(targetUrl);
|
|
8
17
|
if (!proxyUrl) return undefined;
|
|
@@ -11,7 +20,7 @@ export async function getProxyDispatcher(targetUrl) {
|
|
|
11
20
|
return cachedDispatcher;
|
|
12
21
|
}
|
|
13
22
|
|
|
14
|
-
const { ProxyAgent } = await
|
|
23
|
+
const { ProxyAgent } = await importUndici();
|
|
15
24
|
cachedDispatcher = new ProxyAgent(proxyUrl);
|
|
16
25
|
cachedDispatcherProxyUrl = proxyUrl;
|
|
17
26
|
return cachedDispatcher;
|
|
@@ -29,7 +38,7 @@ export async function createProxyWebSocket(url, protocols) {
|
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
const dispatcher = await getProxyDispatcher(url);
|
|
32
|
-
const { WebSocket: UndiciWebSocket } = await
|
|
41
|
+
const { WebSocket: UndiciWebSocket } = await importUndici();
|
|
33
42
|
|
|
34
43
|
const wsOptions = { dispatcher };
|
|
35
44
|
if (protocols) {
|
|
@@ -43,113 +52,22 @@ export async function createProxyWebSocket(url, protocols) {
|
|
|
43
52
|
return new UndiciWebSocket(url, wsOptions);
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
export function getWebSocketImpl(targetUrl) {
|
|
55
|
+
export async function getWebSocketImpl(targetUrl) {
|
|
47
56
|
const proxyUrl = getProxyUrlForTarget(targetUrl);
|
|
48
57
|
if (!proxyUrl) return globalThis.WebSocket;
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (!proxyUrlForTarget) {
|
|
53
|
-
return new globalThis.WebSocket(url, protocols);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const wsPromise = (async () => {
|
|
57
|
-
const dispatcher = await getProxyDispatcher(url);
|
|
58
|
-
const { WebSocket: UndiciWebSocket } = await import('undici');
|
|
59
|
+
const dispatcher = await getProxyDispatcher(targetUrl);
|
|
60
|
+
const { WebSocket: UndiciWebSocket } = await importUndici();
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
class ProxyWebSocket extends UndiciWebSocket {
|
|
63
|
+
constructor(url, protocols) {
|
|
64
|
+
const options = { dispatcher };
|
|
61
65
|
if (protocols) {
|
|
62
|
-
|
|
63
|
-
wsOptions.protocols = protocols;
|
|
64
|
-
} else {
|
|
65
|
-
wsOptions.protocols = [protocols];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return new UndiciWebSocket(url, wsOptions);
|
|
70
|
-
})();
|
|
71
|
-
|
|
72
|
-
return createSyncProxyWebSocketWrapper(wsPromise);
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function createSyncProxyWebSocketWrapper(wsPromise) {
|
|
77
|
-
const pendingEvents = [];
|
|
78
|
-
const eventHandlers = new Map();
|
|
79
|
-
let ws = null;
|
|
80
|
-
let settled = false;
|
|
81
|
-
let readyState = 0;
|
|
82
|
-
let binaryType = 'arraybuffer';
|
|
83
|
-
|
|
84
|
-
const wrapper = {
|
|
85
|
-
get readyState() { return ws ? ws.readyState : readyState; },
|
|
86
|
-
get url() { return ws ? ws.url : ''; },
|
|
87
|
-
get protocol() { return ws ? ws.protocol : ''; },
|
|
88
|
-
get binaryType() { return ws ? ws.binaryType : binaryType; },
|
|
89
|
-
set binaryType(v) {
|
|
90
|
-
binaryType = v;
|
|
91
|
-
if (ws) ws.binaryType = v;
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
send(data, callback) {
|
|
95
|
-
if (ws) {
|
|
96
|
-
if (ws.send.length >= 2) return ws.send(data, callback);
|
|
97
|
-
try { ws.send(data); if (callback) callback(null); } catch (e) { if (callback) callback(e); }
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
pendingEvents.push({ type: 'send', data, callback });
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
close(code, reason) {
|
|
104
|
-
if (ws) { ws.close(code, reason); return; }
|
|
105
|
-
readyState = 3;
|
|
106
|
-
pendingEvents.push({ type: 'close', code, reason });
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
addEventListener(type, handler) {
|
|
110
|
-
if (!eventHandlers.has(type)) eventHandlers.set(type, new Set());
|
|
111
|
-
eventHandlers.get(type).add(handler);
|
|
112
|
-
if (ws) ws.addEventListener(type, handler);
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
removeEventListener(type, handler) {
|
|
116
|
-
const handlers = eventHandlers.get(type);
|
|
117
|
-
if (handlers) handlers.delete(handler);
|
|
118
|
-
if (ws) ws.removeEventListener(type, handler);
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
on(type, handler) { wrapper.addEventListener(type, handler); },
|
|
122
|
-
off(type, handler) { wrapper.removeEventListener(type, handler); },
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
function flushPendingEvents() {
|
|
126
|
-
for (const evt of pendingEvents) {
|
|
127
|
-
if (evt.type === 'send') {
|
|
128
|
-
if (ws.send.length >= 2) ws.send(evt.data, evt.callback);
|
|
129
|
-
else { try { ws.send(evt.data); if (evt.callback) evt.callback(null); } catch (e) { if (evt.callback) evt.callback(e); } }
|
|
130
|
-
} else if (evt.type === 'close') {
|
|
131
|
-
ws.close(evt.code, evt.reason);
|
|
66
|
+
options.protocols = Array.isArray(protocols) ? protocols : [protocols];
|
|
132
67
|
}
|
|
68
|
+
super(url, options);
|
|
133
69
|
}
|
|
134
|
-
pendingEvents.length = 0;
|
|
135
70
|
}
|
|
136
71
|
|
|
137
|
-
|
|
138
|
-
ws = resolvedWs;
|
|
139
|
-
if ('binaryType' in ws) ws.binaryType = binaryType;
|
|
140
|
-
for (const [type, handlers] of eventHandlers) {
|
|
141
|
-
for (const handler of handlers) {
|
|
142
|
-
ws.addEventListener(type, handler);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
flushPendingEvents();
|
|
146
|
-
}).catch((err) => {
|
|
147
|
-
readyState = 3;
|
|
148
|
-
const handlers = eventHandlers.get('error');
|
|
149
|
-
if (handlers) for (const h of handlers) h(err instanceof Error ? err : new Error(String(err)));
|
|
150
|
-
const closeHandlers = eventHandlers.get('close');
|
|
151
|
-
if (closeHandlers) for (const h of closeHandlers) h({ code: 1006, reason: err.message || String(err) });
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return wrapper;
|
|
72
|
+
return ProxyWebSocket;
|
|
155
73
|
}
|
|
@@ -47,6 +47,10 @@ function shouldBypassProxy(hostname, noProxyList) {
|
|
|
47
47
|
if (!p) continue;
|
|
48
48
|
if (p === lower) return true;
|
|
49
49
|
if (p === '*') return true;
|
|
50
|
+
if (p.startsWith('*.')) {
|
|
51
|
+
const domain = p.slice(1);
|
|
52
|
+
if (lower.endsWith(domain) || lower === p.slice(2)) return true;
|
|
53
|
+
}
|
|
50
54
|
if (p.startsWith('.') && lower.endsWith(p)) return true;
|
|
51
55
|
if (lower.endsWith('.' + p)) return true;
|
|
52
56
|
}
|
|
@@ -60,13 +64,16 @@ export function getProxySettings(targetUrl) {
|
|
|
60
64
|
|
|
61
65
|
const fileConfig = readProxyConfig();
|
|
62
66
|
|
|
63
|
-
const https_proxy = envHttps ||
|
|
64
|
-
const http_proxy = envHttp ||
|
|
65
|
-
const no_proxy = envNoProxy ||
|
|
67
|
+
const https_proxy = envHttps || fileConfig?.https_proxy || fileConfig?.HTTPS_PROXY || '';
|
|
68
|
+
const http_proxy = envHttp || fileConfig?.http_proxy || fileConfig?.HTTP_PROXY || '';
|
|
69
|
+
const no_proxy = envNoProxy || fileConfig?.no_proxy || fileConfig?.NO_PROXY || '';
|
|
66
70
|
|
|
67
71
|
if (!https_proxy && !http_proxy) return null;
|
|
68
72
|
|
|
69
|
-
const noProxyList = no_proxy
|
|
73
|
+
const noProxyList = no_proxy
|
|
74
|
+
.split(',')
|
|
75
|
+
.map((s) => s.trim())
|
|
76
|
+
.filter(Boolean);
|
|
70
77
|
|
|
71
78
|
if (targetUrl) {
|
|
72
79
|
const parsed = new URL(targetUrl);
|
|
@@ -18,7 +18,10 @@ export function loadRiskRules(options = {}) {
|
|
|
18
18
|
|
|
19
19
|
function redactEvidence(text) {
|
|
20
20
|
return String(text)
|
|
21
|
-
.replace(
|
|
21
|
+
.replace(
|
|
22
|
+
/((?:access[_-]?key|secret[_-]?key|security[_-]?token|x[_-]?auth[_-]?token|authorization|password|passwd|adminPass|credential)\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,;]+)/gi,
|
|
23
|
+
'$1<redacted>',
|
|
24
|
+
)
|
|
22
25
|
.replace(/(AK|SK)\s*[:=]\s*("[^"]*"|'[^']*'|[^\s,;]+)/g, '$1=<redacted>');
|
|
23
26
|
}
|
|
24
27
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
4
5
|
import { evaluateCommandRisk, mergeRiskDecision } from './risk-rule-engine.mjs';
|
|
5
6
|
|
|
6
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -17,9 +18,13 @@ function regexFrom(pattern) {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
function isSecretKeyName(key, policy = DEFAULT_POLICY) {
|
|
20
|
-
const normalized = String(key)
|
|
21
|
+
const normalized = String(key)
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.replace(/[^a-z0-9]/g, '');
|
|
21
24
|
if (
|
|
22
|
-
/access.*key|secret.*key|security.*token|xauth.*token|authorization|password|passwd|adminpass|credential|private.*key/.test(
|
|
25
|
+
/access.*key|secret.*key|security.*token|xauth.*token|authorization|password|passwd|adminpass|credential|private.*key/.test(
|
|
26
|
+
normalized,
|
|
27
|
+
)
|
|
23
28
|
) {
|
|
24
29
|
return true;
|
|
25
30
|
}
|
|
@@ -28,7 +33,10 @@ function isSecretKeyName(key, policy = DEFAULT_POLICY) {
|
|
|
28
33
|
|
|
29
34
|
function redactString(text) {
|
|
30
35
|
return String(text)
|
|
31
|
-
.replace(
|
|
36
|
+
.replace(
|
|
37
|
+
/((?:access[_-]?key|secret[_-]?key|security[_-]?token|x[_-]?auth[_-]?token|authorization|password|passwd|adminPass|credential)\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,;]+)/gi,
|
|
38
|
+
'$1<redacted>',
|
|
39
|
+
)
|
|
32
40
|
.replace(/(AK|SK)\s*[:=]\s*("[^"]*"|'[^']*'|[^\s,;]+)/g, '$1=<redacted>');
|
|
33
41
|
}
|
|
34
42
|
|
|
@@ -78,15 +86,11 @@ function matchesAny(value, patterns) {
|
|
|
78
86
|
|
|
79
87
|
function hasWritePrefix(operation, policy) {
|
|
80
88
|
const normalized = String(operation);
|
|
81
|
-
return policy.writeOperationPrefixes.some((prefix) =>
|
|
82
|
-
new RegExp(`(^|[A-Za-z0-9])${prefix}`, 'i').test(normalized),
|
|
83
|
-
);
|
|
89
|
+
return policy.writeOperationPrefixes.some((prefix) => new RegExp(`(^|[A-Za-z0-9])${prefix}`, 'i').test(normalized));
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
function hasReadPrefix(operation, policy) {
|
|
87
|
-
return policy.readOperationPrefixes.some((prefix) =>
|
|
88
|
-
new RegExp(`^${prefix}`, 'i').test(operation),
|
|
89
|
-
);
|
|
93
|
+
return policy.readOperationPrefixes.some((prefix) => new RegExp(`^${prefix}`, 'i').test(operation));
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
function isLocalMetadataCommand(args) {
|
|
@@ -119,26 +123,31 @@ export function classifyHcloudArgs(args, options = {}) {
|
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
if (isLocalMetadataCommand(normalizedArgs)) {
|
|
122
|
-
return applyCommandRiskRules(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
return applyCommandRiskRules(
|
|
127
|
+
{
|
|
128
|
+
decision: 'allow',
|
|
129
|
+
risk: 'local_metadata',
|
|
130
|
+
reason: 'KooCLI local help and version commands are read-only and do not call Huawei Cloud resource APIs.',
|
|
131
|
+
service,
|
|
132
|
+
operation,
|
|
133
|
+
args: normalizedArgs,
|
|
134
|
+
},
|
|
135
|
+
normalizedArgs,
|
|
136
|
+
options,
|
|
137
|
+
);
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
if (service.toLowerCase() === 'configure') {
|
|
133
141
|
const subcommand = operation.toLowerCase();
|
|
134
142
|
if (
|
|
135
|
-
policy.blockedConfigureSubcommands.map((cmd) => cmd.toLowerCase()).includes(subcommand)
|
|
136
|
-
|
|
143
|
+
policy.blockedConfigureSubcommands.map((cmd) => cmd.toLowerCase()).includes(subcommand) &&
|
|
144
|
+
options.allowCredentialRead !== true
|
|
137
145
|
) {
|
|
138
146
|
return {
|
|
139
147
|
decision: 'deny',
|
|
140
148
|
risk: 'credential',
|
|
141
|
-
reason:
|
|
149
|
+
reason:
|
|
150
|
+
'Direct hcloud configure inspection may expose profile credentials. Use the redacted toolkit tools instead.',
|
|
142
151
|
};
|
|
143
152
|
}
|
|
144
153
|
}
|
|
@@ -160,7 +169,8 @@ export function classifyHcloudArgs(args, options = {}) {
|
|
|
160
169
|
}
|
|
161
170
|
|
|
162
171
|
const readOnly = hasReadPrefix(operation, policy);
|
|
163
|
-
const executionOps =
|
|
172
|
+
const executionOps =
|
|
173
|
+
/(^|\.)(Invoke|SyncInvoke|AsyncInvoke|Send|Trigger|Execute|Start|Reboot|Restart|Stop|Publish|Deploy)/i;
|
|
164
174
|
const isExecution = executionOps.test(operation) && !readOnly;
|
|
165
175
|
const isWrite = !readOnly && hasWritePrefix(operation, policy);
|
|
166
176
|
|
|
@@ -176,11 +186,30 @@ export function classifyHcloudArgs(args, options = {}) {
|
|
|
176
186
|
return {
|
|
177
187
|
decision: 'deny',
|
|
178
188
|
risk: 'write',
|
|
179
|
-
reason:
|
|
189
|
+
reason:
|
|
190
|
+
'Huawei Cloud write operation blocked until the agent presents a plan and receives explicit user approval.',
|
|
180
191
|
};
|
|
181
192
|
}
|
|
182
193
|
|
|
183
|
-
const obsutilWrites = [
|
|
194
|
+
const obsutilWrites = [
|
|
195
|
+
'mb',
|
|
196
|
+
'cp',
|
|
197
|
+
'mv',
|
|
198
|
+
'rm',
|
|
199
|
+
'delete',
|
|
200
|
+
'mkdir',
|
|
201
|
+
'sync',
|
|
202
|
+
'restore',
|
|
203
|
+
'chattri',
|
|
204
|
+
'bucketpolicy',
|
|
205
|
+
'lifecycle',
|
|
206
|
+
'cors',
|
|
207
|
+
'website',
|
|
208
|
+
'sign',
|
|
209
|
+
'share-add',
|
|
210
|
+
'share-update',
|
|
211
|
+
'share-rm',
|
|
212
|
+
];
|
|
184
213
|
const obsutilReads = ['ls', 'stat', 'cat', 'help', 'version'];
|
|
185
214
|
const isObs = service.toLowerCase() === 'obs' || service.toLowerCase() === 'hcloud obs';
|
|
186
215
|
const isObsWrite = isObs && obsutilWrites.includes(operation);
|
|
@@ -193,64 +222,86 @@ export function classifyHcloudArgs(args, options = {}) {
|
|
|
193
222
|
};
|
|
194
223
|
}
|
|
195
224
|
if (isObsRead) {
|
|
196
|
-
return applyCommandRiskRules(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
225
|
+
return applyCommandRiskRules(
|
|
226
|
+
{
|
|
227
|
+
decision: 'allow',
|
|
228
|
+
risk: 'read_only',
|
|
229
|
+
reason: 'OBS read-only operation.',
|
|
230
|
+
service,
|
|
231
|
+
operation,
|
|
232
|
+
args: normalizedArgs,
|
|
233
|
+
},
|
|
234
|
+
normalizedArgs,
|
|
235
|
+
options,
|
|
236
|
+
);
|
|
204
237
|
}
|
|
205
238
|
if (isObsWrite && options.allowWrites) {
|
|
206
|
-
return applyCommandRiskRules(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
239
|
+
return applyCommandRiskRules(
|
|
240
|
+
{
|
|
241
|
+
decision: 'allow',
|
|
242
|
+
risk: 'write',
|
|
243
|
+
reason: 'OBS write operation approved by user.',
|
|
244
|
+
service,
|
|
245
|
+
operation,
|
|
246
|
+
args: normalizedArgs,
|
|
247
|
+
},
|
|
248
|
+
normalizedArgs,
|
|
249
|
+
options,
|
|
250
|
+
);
|
|
214
251
|
}
|
|
215
252
|
|
|
216
253
|
if (isExecution && options.allowWrites) {
|
|
217
|
-
return applyCommandRiskRules(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
254
|
+
return applyCommandRiskRules(
|
|
255
|
+
{
|
|
256
|
+
decision: 'allow',
|
|
257
|
+
risk: 'execution',
|
|
258
|
+
reason: 'Huawei Cloud execution/trigger operation approved by user.',
|
|
259
|
+
service,
|
|
260
|
+
operation,
|
|
261
|
+
args: normalizedArgs,
|
|
262
|
+
},
|
|
263
|
+
normalizedArgs,
|
|
264
|
+
options,
|
|
265
|
+
);
|
|
225
266
|
}
|
|
226
267
|
|
|
227
268
|
if (isWrite && options.allowWrites) {
|
|
228
|
-
return applyCommandRiskRules(
|
|
269
|
+
return applyCommandRiskRules(
|
|
270
|
+
{
|
|
271
|
+
decision: 'allow',
|
|
272
|
+
risk: 'write',
|
|
273
|
+
reason: 'Huawei Cloud write operation approved by user.',
|
|
274
|
+
service,
|
|
275
|
+
operation,
|
|
276
|
+
args: normalizedArgs,
|
|
277
|
+
},
|
|
278
|
+
normalizedArgs,
|
|
279
|
+
options,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return applyCommandRiskRules(
|
|
284
|
+
{
|
|
229
285
|
decision: 'allow',
|
|
230
|
-
risk: '
|
|
231
|
-
reason:
|
|
286
|
+
risk: readOnly ? 'read_only' : 'unknown_read',
|
|
287
|
+
reason: readOnly
|
|
288
|
+
? 'Command appears to be a read-only Huawei Cloud operation.'
|
|
289
|
+
: 'Command does not match a known write or secret operation; treat output as untrusted and redact it.',
|
|
232
290
|
service,
|
|
233
291
|
operation,
|
|
234
292
|
args: normalizedArgs,
|
|
235
|
-
},
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
decision: 'allow',
|
|
240
|
-
risk: readOnly ? 'read_only' : 'unknown_read',
|
|
241
|
-
reason: readOnly
|
|
242
|
-
? 'Command appears to be a read-only Huawei Cloud operation.'
|
|
243
|
-
: 'Command does not match a known write or secret operation; treat output as untrusted and redact it.',
|
|
244
|
-
service,
|
|
245
|
-
operation,
|
|
246
|
-
args: normalizedArgs,
|
|
247
|
-
}, normalizedArgs, options);
|
|
293
|
+
},
|
|
294
|
+
normalizedArgs,
|
|
295
|
+
options,
|
|
296
|
+
);
|
|
248
297
|
}
|
|
249
298
|
|
|
250
299
|
function splitSimpleCommand(command) {
|
|
251
|
-
return
|
|
252
|
-
|
|
253
|
-
|
|
300
|
+
return (
|
|
301
|
+
String(command)
|
|
302
|
+
.match(/"[^"]*"|'[^']*'|\S+/g)
|
|
303
|
+
?.map((part) => part.replace(/^['"]|['"]$/g, '')) || []
|
|
304
|
+
);
|
|
254
305
|
}
|
|
255
306
|
|
|
256
307
|
export function classifyTextCommand(command, options = {}) {
|
|
@@ -261,11 +312,15 @@ export function classifyTextCommand(command, options = {}) {
|
|
|
261
312
|
return {
|
|
262
313
|
decision: 'deny',
|
|
263
314
|
risk: 'credential',
|
|
264
|
-
reason:
|
|
315
|
+
reason:
|
|
316
|
+
'Reading Huawei Cloud credential or profile files is blocked. Use redacted profile inspection tools instead.',
|
|
265
317
|
};
|
|
266
318
|
}
|
|
267
319
|
|
|
268
|
-
if (
|
|
320
|
+
if (
|
|
321
|
+
/(^|\s)(env|printenv|Get-ChildItem\s+Env:|gci\s+Env:|dir\s+Env:)/i.test(text) &&
|
|
322
|
+
/HUAWEICLOUD|HWC_|HCLOUD|OS_/i.test(text)
|
|
323
|
+
) {
|
|
269
324
|
return {
|
|
270
325
|
decision: 'deny',
|
|
271
326
|
risk: 'credential',
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { getCredentials } from './hwlink-api.mjs';
|
|
2
2
|
import { getProxyDispatcher } from '../proxy/proxy-agent.mjs';
|
|
3
|
+
import { cacheUserHash } from '../telemetry/telemetry.mjs';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
process.env.HDKITSERVICE_ENDPOINT ||
|
|
6
|
-
|
|
5
|
+
function getHdkitBaseUrl() {
|
|
6
|
+
return process.env.HDKITSERVICE_ENDPOINT || 'https://devkit.huaweicloud.com/rest/developer/server/hdkitservice/';
|
|
7
|
+
}
|
|
7
8
|
|
|
8
9
|
async function hdkitRequest(method, path, body, timeoutMs = 300000) {
|
|
9
10
|
const { ak, sk, securitytoken } = getCredentials();
|
|
10
11
|
|
|
12
|
+
if (!ak || !sk) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
'Huawei Cloud credentials are not configured. ' +
|
|
15
|
+
'Run "npx huaweicloud-devkit auth init" or set HW_ACCESS_KEY/HW_SECRET_KEY.',
|
|
16
|
+
);
|
|
17
|
+
}
|
|
11
18
|
const headers = {
|
|
12
19
|
'Content-Type': 'application/json',
|
|
13
20
|
'X-HW-AK': ak,
|
|
@@ -17,7 +24,7 @@ async function hdkitRequest(method, path, body, timeoutMs = 300000) {
|
|
|
17
24
|
headers['X-HW-Security-Token'] = securitytoken;
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
const url = `${
|
|
27
|
+
const url = `${getHdkitBaseUrl()}${path}`;
|
|
21
28
|
const controller = new AbortController();
|
|
22
29
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
23
30
|
|
|
@@ -30,8 +37,13 @@ async function hdkitRequest(method, path, body, timeoutMs = 300000) {
|
|
|
30
37
|
body: body ? JSON.stringify(body) : undefined,
|
|
31
38
|
signal: controller.signal,
|
|
32
39
|
};
|
|
33
|
-
if (dispatcher)
|
|
34
|
-
|
|
40
|
+
if (dispatcher) {
|
|
41
|
+
fetchOpts.dispatcher = dispatcher;
|
|
42
|
+
const { fetch: undiciFetch } = await import('undici');
|
|
43
|
+
resp = await undiciFetch(url, fetchOpts);
|
|
44
|
+
} else {
|
|
45
|
+
resp = await fetch(url, fetchOpts);
|
|
46
|
+
}
|
|
35
47
|
} finally {
|
|
36
48
|
clearTimeout(timer);
|
|
37
49
|
}
|
|
@@ -45,12 +57,12 @@ async function hdkitRequest(method, path, body, timeoutMs = 300000) {
|
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
if (!resp.ok) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
);
|
|
51
|
-
err.code =
|
|
60
|
+
const code = data.code || `HTTP_${resp.status}`;
|
|
61
|
+
const trace = data.traceId ? ` [trace: ${data.traceId}]` : '';
|
|
62
|
+
const err = new Error(`${code}: ${data.message || 'hdkitservice error'}${trace}`);
|
|
63
|
+
err.code = code;
|
|
52
64
|
err.status = resp.status;
|
|
53
|
-
err.traceId = data.traceId;
|
|
65
|
+
err.traceId = data.traceId;
|
|
54
66
|
throw err;
|
|
55
67
|
}
|
|
56
68
|
|
|
@@ -58,7 +70,15 @@ async function hdkitRequest(method, path, body, timeoutMs = 300000) {
|
|
|
58
70
|
}
|
|
59
71
|
|
|
60
72
|
export async function hdkitCheckUser() {
|
|
61
|
-
|
|
73
|
+
const result = await hdkitRequest('GET', 'check-user', undefined, 30000);
|
|
74
|
+
if (result.userHash) cacheUserHash(result.userHash);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function hdkitGenerateUserHash() {
|
|
79
|
+
const result = await hdkitRequest('GET', 'user/generatorUserIDHash', undefined, 30000);
|
|
80
|
+
if (result.userHash) cacheUserHash(result.userHash);
|
|
81
|
+
return result;
|
|
62
82
|
}
|
|
63
83
|
|
|
64
84
|
export async function hdkitSignAgreement() {
|
|
@@ -88,4 +108,20 @@ export async function hdkitCredentials(sessionId, devStageId, enableSts = true)
|
|
|
88
108
|
return await hdkitRequest('POST', 'credentials', body);
|
|
89
109
|
}
|
|
90
110
|
|
|
111
|
+
export async function hdkitVoucherStatus(domainId) {
|
|
112
|
+
try {
|
|
113
|
+
const path = domainId ? `voucher/status?domain_id=${encodeURIComponent(domainId)}` : 'voucher/status';
|
|
114
|
+
return await hdkitRequest('GET', path, undefined, 30000);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return { claimed: false, message: 'Incentive service unavailable, please try again later' };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
91
119
|
|
|
120
|
+
export async function hdkitVoucherClaim(domainId) {
|
|
121
|
+
try {
|
|
122
|
+
const body = domainId ? { domain_id: domainId } : {};
|
|
123
|
+
return await hdkitRequest('POST', 'voucher/claim', body);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return { claimed: false, message: 'Incentive service unavailable, please try again later' };
|
|
126
|
+
}
|
|
127
|
+
}
|