@m1a0rz/agent-identity 0.1.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.
Files changed (125) hide show
  1. package/README-cn.md +223 -0
  2. package/README.md +223 -0
  3. package/dist/index.d.ts +14 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +306 -0
  6. package/dist/src/actions/identity-actions.d.ts +142 -0
  7. package/dist/src/actions/identity-actions.d.ts.map +1 -0
  8. package/dist/src/actions/identity-actions.js +429 -0
  9. package/dist/src/commands/identity-commands.d.ts +33 -0
  10. package/dist/src/commands/identity-commands.d.ts.map +1 -0
  11. package/dist/src/commands/identity-commands.js +572 -0
  12. package/dist/src/hooks/after-tool-call.d.ts +22 -0
  13. package/dist/src/hooks/after-tool-call.d.ts.map +1 -0
  14. package/dist/src/hooks/after-tool-call.js +35 -0
  15. package/dist/src/hooks/before-agent-start.d.ts +30 -0
  16. package/dist/src/hooks/before-agent-start.d.ts.map +1 -0
  17. package/dist/src/hooks/before-agent-start.js +93 -0
  18. package/dist/src/hooks/before-tool-call.d.ts +38 -0
  19. package/dist/src/hooks/before-tool-call.d.ts.map +1 -0
  20. package/dist/src/hooks/before-tool-call.js +138 -0
  21. package/dist/src/risk/classify-risk.d.ts +24 -0
  22. package/dist/src/risk/classify-risk.d.ts.map +1 -0
  23. package/dist/src/risk/classify-risk.js +61 -0
  24. package/dist/src/risk/diagnose-risk.d.ts +21 -0
  25. package/dist/src/risk/diagnose-risk.d.ts.map +1 -0
  26. package/dist/src/risk/diagnose-risk.js +37 -0
  27. package/dist/src/risk/llm-risk-check.d.ts +27 -0
  28. package/dist/src/risk/llm-risk-check.d.ts.map +1 -0
  29. package/dist/src/risk/llm-risk-check.js +274 -0
  30. package/dist/src/risk/low-risk-tools.d.ts +5 -0
  31. package/dist/src/risk/low-risk-tools.d.ts.map +1 -0
  32. package/dist/src/risk/low-risk-tools.js +29 -0
  33. package/dist/src/routes/oidc-login.d.ts +51 -0
  34. package/dist/src/routes/oidc-login.d.ts.map +1 -0
  35. package/dist/src/routes/oidc-login.js +153 -0
  36. package/dist/src/services/identity-client.d.ts +366 -0
  37. package/dist/src/services/identity-client.d.ts.map +1 -0
  38. package/dist/src/services/identity-client.js +578 -0
  39. package/dist/src/services/identity-credentials.d.ts +28 -0
  40. package/dist/src/services/identity-credentials.d.ts.map +1 -0
  41. package/dist/src/services/identity-credentials.js +170 -0
  42. package/dist/src/services/identity-service.d.ts +33 -0
  43. package/dist/src/services/identity-service.d.ts.map +1 -0
  44. package/dist/src/services/identity-service.js +53 -0
  45. package/dist/src/services/oidc-client.d.ts +57 -0
  46. package/dist/src/services/oidc-client.d.ts.map +1 -0
  47. package/dist/src/services/oidc-client.js +127 -0
  48. package/dist/src/services/send-notification-feishu.d.ts +27 -0
  49. package/dist/src/services/send-notification-feishu.d.ts.map +1 -0
  50. package/dist/src/services/send-notification-feishu.js +148 -0
  51. package/dist/src/services/session-refresh.d.ts +16 -0
  52. package/dist/src/services/session-refresh.d.ts.map +1 -0
  53. package/dist/src/services/session-refresh.js +38 -0
  54. package/dist/src/store/credential-env-bindings.d.ts +16 -0
  55. package/dist/src/store/credential-env-bindings.d.ts.map +1 -0
  56. package/dist/src/store/credential-env-bindings.js +61 -0
  57. package/dist/src/store/credential-store.d.ts +31 -0
  58. package/dist/src/store/credential-store.d.ts.map +1 -0
  59. package/dist/src/store/credential-store.js +57 -0
  60. package/dist/src/store/oidc-state-store.d.ts +15 -0
  61. package/dist/src/store/oidc-state-store.d.ts.map +1 -0
  62. package/dist/src/store/oidc-state-store.js +32 -0
  63. package/dist/src/store/session-store.d.ts +21 -0
  64. package/dist/src/store/session-store.d.ts.map +1 -0
  65. package/dist/src/store/session-store.js +69 -0
  66. package/dist/src/store/tip-store.d.ts +21 -0
  67. package/dist/src/store/tip-store.d.ts.map +1 -0
  68. package/dist/src/store/tip-store.js +60 -0
  69. package/dist/src/store/tool-approval-store.d.ts +44 -0
  70. package/dist/src/store/tool-approval-store.d.ts.map +1 -0
  71. package/dist/src/store/tool-approval-store.js +147 -0
  72. package/dist/src/tools/identity-approve-tool.d.ts +24 -0
  73. package/dist/src/tools/identity-approve-tool.d.ts.map +1 -0
  74. package/dist/src/tools/identity-approve-tool.js +36 -0
  75. package/dist/src/tools/identity-config.d.ts +13 -0
  76. package/dist/src/tools/identity-config.d.ts.map +1 -0
  77. package/dist/src/tools/identity-config.js +18 -0
  78. package/dist/src/tools/identity-fetch.d.ts +21 -0
  79. package/dist/src/tools/identity-fetch.d.ts.map +1 -0
  80. package/dist/src/tools/identity-fetch.js +63 -0
  81. package/dist/src/tools/identity-list-credentials.d.ts +15 -0
  82. package/dist/src/tools/identity-list-credentials.d.ts.map +1 -0
  83. package/dist/src/tools/identity-list-credentials.js +30 -0
  84. package/dist/src/tools/identity-list-risk-patterns.d.ts +13 -0
  85. package/dist/src/tools/identity-list-risk-patterns.d.ts.map +1 -0
  86. package/dist/src/tools/identity-list-risk-patterns.js +23 -0
  87. package/dist/src/tools/identity-list-tips.d.ts +13 -0
  88. package/dist/src/tools/identity-list-tips.d.ts.map +1 -0
  89. package/dist/src/tools/identity-list-tips.js +21 -0
  90. package/dist/src/tools/identity-login.d.ts +14 -0
  91. package/dist/src/tools/identity-login.d.ts.map +1 -0
  92. package/dist/src/tools/identity-login.js +40 -0
  93. package/dist/src/tools/identity-logout.d.ts +13 -0
  94. package/dist/src/tools/identity-logout.d.ts.map +1 -0
  95. package/dist/src/tools/identity-logout.js +24 -0
  96. package/dist/src/tools/identity-risk-check.d.ts +29 -0
  97. package/dist/src/tools/identity-risk-check.d.ts.map +1 -0
  98. package/dist/src/tools/identity-risk-check.js +54 -0
  99. package/dist/src/tools/identity-set-binding.d.ts +16 -0
  100. package/dist/src/tools/identity-set-binding.d.ts.map +1 -0
  101. package/dist/src/tools/identity-set-binding.js +31 -0
  102. package/dist/src/tools/identity-status.d.ts +13 -0
  103. package/dist/src/tools/identity-status.d.ts.map +1 -0
  104. package/dist/src/tools/identity-status.js +41 -0
  105. package/dist/src/tools/identity-unset-binding.d.ts +15 -0
  106. package/dist/src/tools/identity-unset-binding.d.ts.map +1 -0
  107. package/dist/src/tools/identity-unset-binding.js +25 -0
  108. package/dist/src/tools/identity-whoami.d.ts +13 -0
  109. package/dist/src/tools/identity-whoami.d.ts.map +1 -0
  110. package/dist/src/tools/identity-whoami.js +38 -0
  111. package/dist/src/types.d.ts +93 -0
  112. package/dist/src/types.d.ts.map +1 -0
  113. package/dist/src/types.js +5 -0
  114. package/dist/src/utils/approval-channel.d.ts +11 -0
  115. package/dist/src/utils/approval-channel.d.ts.map +1 -0
  116. package/dist/src/utils/approval-channel.js +13 -0
  117. package/dist/src/utils/auth.d.ts +24 -0
  118. package/dist/src/utils/auth.d.ts.map +1 -0
  119. package/dist/src/utils/auth.js +44 -0
  120. package/dist/src/utils/derive-session-key.d.ts +78 -0
  121. package/dist/src/utils/derive-session-key.d.ts.map +1 -0
  122. package/dist/src/utils/derive-session-key.js +198 -0
  123. package/openclaw.plugin.json +162 -0
  124. package/package.json +33 -0
  125. package/skills/SKILL.md +230 -0
@@ -0,0 +1,93 @@
1
+ /**
2
+ * before_agent_start hook: fetch TIP token and inject credentials into process.env.
3
+ * 1. Inject credentials into process.env per credential-env-bindings (per-session)
4
+ * 2. Look up userToken from session store by sessionKey
5
+ * 3. Call CIS GetWorkloadAccessTokenForJWT
6
+ * 4. On "token has expired", refresh userToken via refresh_token grant and retry
7
+ * 5. Store TIP token in tip-store for use by before_tool_call (AuthZ) and downstream
8
+ */
9
+ import { refreshSessionUserToken } from "../services/session-refresh.js";
10
+ import { loadCredentialEnvBindings } from "../store/credential-env-bindings.js";
11
+ import { getCredential, resolveCredentialValue } from "../store/credential-store.js";
12
+ import { getSession } from "../store/session-store.js";
13
+ import { getTIPToken, setTIPToken } from "../store/tip-store.js";
14
+ import { resolveAgentId } from "../utils/derive-session-key.js";
15
+ function isTokenExpiredError(err) {
16
+ const msg = err instanceof Error ? err.message : String(err);
17
+ return /token has expired|Invalid token/i.test(msg);
18
+ }
19
+ export function createBeforeAgentStartHandler(deps) {
20
+ const { storeDir, identityService, getOidcConfigForRefresh, logger } = deps;
21
+ return async (_event, ctx) => {
22
+ const sessionKey = ctx.sessionKey;
23
+ if (!sessionKey)
24
+ return;
25
+ try {
26
+ // 0. Inject credentials into process.env per bindings (clear if no cred to avoid cross-session leak)
27
+ const bindings = await loadCredentialEnvBindings(storeDir, sessionKey);
28
+ for (const [provider, envVar] of Object.entries(bindings)) {
29
+ const cred = await getCredential(storeDir, sessionKey, provider);
30
+ const value = cred ? resolveCredentialValue(cred) : undefined;
31
+ logger?.warn?.(`agent-identity: injecting ${envVar}=${value}`);
32
+ if (value) {
33
+ process.env[envVar] = value;
34
+ }
35
+ else {
36
+ delete process.env[envVar];
37
+ }
38
+ }
39
+ }
40
+ catch {
41
+ // Best-effort; do not block agent start
42
+ }
43
+ try {
44
+ // 1. Check if we already have a valid TIP token cached
45
+ const cached = await getTIPToken(storeDir, sessionKey);
46
+ if (cached)
47
+ return;
48
+ // 2. Look up userToken from session store
49
+ let session = await getSession(storeDir, sessionKey);
50
+ if (!session)
51
+ return;
52
+ let userToken = session.userToken;
53
+ try {
54
+ const agentId = resolveAgentId({
55
+ agentId: ctx.agentId,
56
+ sessionKey,
57
+ });
58
+ const tipEntry = await identityService.getWorkloadAccessToken({
59
+ agentId,
60
+ userToken,
61
+ sub: session.sub,
62
+ });
63
+ // 4. Store TIP token
64
+ await setTIPToken(storeDir, sessionKey, tipEntry);
65
+ logger.info?.(`agent-identity: TIP token acquired for session ${sessionKey.slice(0, 24)}...`);
66
+ }
67
+ catch (err) {
68
+ if (isTokenExpiredError(err) && getOidcConfigForRefresh && session.refreshToken) {
69
+ const refreshed = await refreshSessionUserToken(storeDir, sessionKey, getOidcConfigForRefresh);
70
+ if (refreshed) {
71
+ session = (await getSession(storeDir, sessionKey)) ?? session;
72
+ const agentId = resolveAgentId({
73
+ agentId: ctx.agentId,
74
+ sessionKey,
75
+ });
76
+ const tipEntry = await identityService.getWorkloadAccessToken({
77
+ agentId,
78
+ userToken: refreshed,
79
+ sub: session.sub,
80
+ });
81
+ await setTIPToken(storeDir, sessionKey, tipEntry);
82
+ logger.info?.(`agent-identity: TIP token acquired after user token refresh for ${sessionKey.slice(0, 24)}...`);
83
+ return;
84
+ }
85
+ }
86
+ throw err;
87
+ }
88
+ }
89
+ catch (err) {
90
+ logger.warn?.(`agent-identity: failed to get TIP token for ${sessionKey}: ${String(err)}`);
91
+ }
92
+ };
93
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * before_tool_call hook: optional AuthZ check.
3
+ * When enabled, blocks tool calls for sessions without valid TIP or denied by policy.
4
+ * Integrates with CheckPermission when identityClient is provided (veadk-style).
5
+ * Supports low-risk bypass and high-risk pending approval (Channel: poll, Webchat/TUI: retry).
6
+ *
7
+ * @see https://github.com/volcengine/veadk-python/blob/main/veadk/tools/builtin_tools/agent_authorization.py
8
+ */
9
+ import type { IdentityClientInterface } from "../services/identity-client.js";
10
+ import type { PluginConfig } from "../types.js";
11
+ export type BeforeToolCallDeps = {
12
+ storeDir: string;
13
+ /** When set, call CheckPermission with principal/originalCallers from TIP token, resource=toolName. */
14
+ identityClient?: IdentityClientInterface;
15
+ /** Namespace for CheckPermission. From authz.namespaceName, default "default". */
16
+ namespaceName?: string;
17
+ logger: {
18
+ debug?: (msg: string) => void;
19
+ warn?: (msg: string) => void;
20
+ };
21
+ /** Send message to session (Channel only). For sync approval flow. */
22
+ sendToSession?: (targetOrSessionKey: string, text: string) => Promise<void>;
23
+ /** Authz config for low-risk bypass and risk approval. */
24
+ authz?: PluginConfig["authz"];
25
+ approvalTtlMs: number;
26
+ };
27
+ export declare function createBeforeToolCallHandler(deps: BeforeToolCallDeps): (event: {
28
+ toolName: string;
29
+ params: Record<string, unknown>;
30
+ }, ctx: {
31
+ agentId?: string;
32
+ sessionKey?: string;
33
+ toolName: string;
34
+ }) => Promise<{
35
+ block?: boolean;
36
+ blockReason?: string;
37
+ } | void>;
38
+ //# sourceMappingURL=before-tool-call.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-tool-call.d.ts","sourceRoot":"","sources":["../../../src/hooks/before-tool-call.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQhD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,uGAAuG;IACvG,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IACxE,sEAAsE;IACtE,aAAa,CAAC,EAAE,CAAC,kBAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,0DAA0D;IAC1D,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAoBF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,kBAAkB,IAgBhE,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC5D,KAAK;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAC/D,OAAO,CAAC;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA4I7D"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * before_tool_call hook: optional AuthZ check.
3
+ * When enabled, blocks tool calls for sessions without valid TIP or denied by policy.
4
+ * Integrates with CheckPermission when identityClient is provided (veadk-style).
5
+ * Supports low-risk bypass and high-risk pending approval (Channel: poll, Webchat/TUI: retry).
6
+ *
7
+ * @see https://github.com/volcengine/veadk-python/blob/main/veadk/tools/builtin_tools/agent_authorization.py
8
+ */
9
+ import { diagnoseRisk } from "../risk/diagnose-risk.js";
10
+ import { isLowRiskTool } from "../risk/low-risk-tools.js";
11
+ import { getTIPToken } from "../store/tip-store.js";
12
+ import * as toolApprovalStore from "../store/tool-approval-store.js";
13
+ import { supportsSyncApproval } from "../utils/approval-channel.js";
14
+ import { extractDelegationChainFromJwt } from "../utils/auth.js";
15
+ function buildApprovalMessage(toolName, params, approvalId, ttlSeconds, riskReason) {
16
+ const preview = toolName === "exec" || toolName === "process"
17
+ ? String(params.command ?? params.cmd ?? params.script ?? "").slice(0, 80)
18
+ : String(params.path ?? params.target ?? "").slice(0, 80);
19
+ const reasonLine = riskReason && riskReason.trim()
20
+ ? `\nRisk: ${riskReason.trim()}`
21
+ : "";
22
+ return `Tool "${toolName}"${preview ? ` (${preview}...)` : ""} requires your approval.${reasonLine}\nReply "approve" or /identity approve ${approvalId}. Expires in ${ttlSeconds}s.`;
23
+ }
24
+ export function createBeforeToolCallHandler(deps) {
25
+ const { storeDir, identityClient, namespaceName = "default", logger, sendToSession, authz, approvalTtlMs, } = deps;
26
+ const lowRiskBypass = authz?.lowRiskBypass !== false;
27
+ const requireRiskApproval = authz?.requireRiskApproval !== false;
28
+ const extraLowRisk = authz?.lowRiskTools;
29
+ return async (event, ctx) => {
30
+ const { toolName, params } = event;
31
+ const sessionKey = ctx.sessionKey;
32
+ logger?.debug?.(`agent-identity: before_tool_call: toolName=${toolName}`);
33
+ if (!sessionKey)
34
+ return;
35
+ if (lowRiskBypass && isLowRiskTool(toolName, extraLowRisk)) {
36
+ logger?.debug?.(`agent-identity: low-risk bypass for ${toolName}`);
37
+ return;
38
+ }
39
+ const tip = await getTIPToken(storeDir, sessionKey);
40
+ if (!tip) {
41
+ return {
42
+ block: true,
43
+ blockReason: "AuthZ: session has no valid identity (TIP token required)",
44
+ };
45
+ }
46
+ if (identityClient) {
47
+ const chain = extractDelegationChainFromJwt(tip.token);
48
+ if (!chain) {
49
+ return {
50
+ block: true,
51
+ blockReason: "AuthZ: failed to parse delegation chain from TIP token",
52
+ };
53
+ }
54
+ const principal = { Type: "user", Id: chain.principalId };
55
+ const action = { Type: "Action", Id: "invoke" };
56
+ const resource = { Type: "tool", Id: toolName };
57
+ const originalCallers = chain.actors.map((id) => ({
58
+ Type: "agent",
59
+ Id: id,
60
+ }));
61
+ logger?.debug?.(`agent-identity: before_tool_call: checking permission for ${toolName} (sub: ${tip.sub}), originalCallers: ${originalCallers.map((c) => c.Id).join(", ")}`);
62
+ try {
63
+ const result = await identityClient.checkPermission({
64
+ namespaceName,
65
+ principal,
66
+ action,
67
+ resource,
68
+ originalCallers,
69
+ });
70
+ if (!result.allowed) {
71
+ return {
72
+ block: true,
73
+ blockReason: result.message || `AuthZ: CheckPermission denied for tool ${toolName}`,
74
+ };
75
+ }
76
+ }
77
+ catch (err) {
78
+ logger?.debug?.(`agent-identity: CheckPermission error: ${String(err)}`);
79
+ return {
80
+ block: true,
81
+ blockReason: `AuthZ: Failed to verify permission: ${String(err)}`,
82
+ };
83
+ }
84
+ }
85
+ if (!requireRiskApproval) {
86
+ logger?.debug?.(`agent-identity: AuthZ ok for ${toolName} (sub: ${tip.sub})`);
87
+ return;
88
+ }
89
+ const paramsRecord = params && typeof params === "object" ? params : {};
90
+ const llmConfig = authz?.enableLlmRiskCheck && authz?.llmRiskCheck ? authz.llmRiskCheck : undefined;
91
+ const { risk, reason: riskReason } = await diagnoseRisk(toolName, paramsRecord, llmConfig, logger);
92
+ if (risk !== "high") {
93
+ logger?.debug?.(`agent-identity: AuthZ ok for ${toolName} (risk=${risk})`);
94
+ return;
95
+ }
96
+ if (toolApprovalStore.hasRecentApproval(sessionKey, toolName, paramsRecord)) {
97
+ toolApprovalStore.consumeApproval(sessionKey, toolName, paramsRecord);
98
+ logger?.debug?.(`agent-identity: AuthZ ok for ${toolName} (recent approval)`);
99
+ return;
100
+ }
101
+ const fullHash = toolApprovalStore.hashToolParams(toolName, paramsRecord);
102
+ const approvalId = toolApprovalStore.shortApprovalId(fullHash + sessionKey + Date.now());
103
+ const ttlSeconds = Math.floor(approvalTtlMs / 1000);
104
+ if (supportsSyncApproval(sessionKey) && sendToSession) {
105
+ toolApprovalStore.createPending({
106
+ approvalId,
107
+ sessionKey,
108
+ toolName,
109
+ params: paramsRecord,
110
+ ttlMs: approvalTtlMs,
111
+ });
112
+ await sendToSession(sessionKey, buildApprovalMessage(toolName, paramsRecord, approvalId, ttlSeconds, riskReason));
113
+ const approved = await toolApprovalStore.pollForApproval(approvalId, approvalTtlMs);
114
+ if (approved) {
115
+ logger?.debug?.(`agent-identity: AuthZ ok for ${toolName} (approved via poll)`);
116
+ return;
117
+ }
118
+ return {
119
+ block: true,
120
+ blockReason: riskReason
121
+ ? `AuthZ: High-risk tool requires approval. ${riskReason} Approval timed out or rejected.`
122
+ : "AuthZ: High-risk tool requires approval. Approval timed out or rejected.",
123
+ };
124
+ }
125
+ toolApprovalStore.createPending({
126
+ approvalId,
127
+ sessionKey,
128
+ toolName,
129
+ params: paramsRecord,
130
+ ttlMs: approvalTtlMs,
131
+ });
132
+ const reasonSuffix = riskReason ? ` Reason: ${riskReason}.` : "";
133
+ return {
134
+ block: true,
135
+ blockReason: `AuthZ: High-risk tool requires approval. Run /identity approve ${approvalId}, then retry.${reasonSuffix}`,
136
+ };
137
+ };
138
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Rule-based risk classification for tool calls.
3
+ * Used after CheckPermission to determine if user approval is required.
4
+ */
5
+ export type RiskLevel = "low" | "medium" | "high";
6
+ export type RuleRiskResult = {
7
+ risk: RiskLevel;
8
+ reason?: string;
9
+ };
10
+ /**
11
+ * Classify risk based on built-in rules only.
12
+ */
13
+ export declare function classifyRiskRules(toolName: string, params: Record<string, unknown>): RuleRiskResult;
14
+ /**
15
+ * Return built-in risk patterns for display (e.g. identity_list_risk_patterns).
16
+ */
17
+ export declare function getRiskPatterns(): {
18
+ commandPatterns: Array<{
19
+ example: string;
20
+ reason: string;
21
+ }>;
22
+ sensitivePaths: string[];
23
+ };
24
+ //# sourceMappingURL=classify-risk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify-risk.d.ts","sourceRoot":"","sources":["../../../src/risk/classify-risk.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA+BlE;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,cAAc,CAkBhB;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI;IACjC,eAAe,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,CAQA"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Rule-based risk classification for tool calls.
3
+ * Used after CheckPermission to determine if user approval is required.
4
+ */
5
+ const DANGEROUS_COMMAND_PATTERNS = [
6
+ { pattern: /\brm\s+-rf\b/i, reason: "Destructive recursive delete", example: "rm -rf /" },
7
+ { pattern: /\bsudo\b/i, reason: "Privilege escalation", example: "sudo apt install" },
8
+ { pattern: /\bchmod\s+[0-7]{3,4}\b/i, reason: "Permission change on filesystem", example: "chmod 777 file" },
9
+ { pattern: />\s*\/dev\/(sd|nvme|vd)/i, reason: "Direct block device access", example: ">/dev/sda" },
10
+ { pattern: /\bdd\s+if=/i, reason: "Raw disk write", example: "dd if=/dev/zero of=..." },
11
+ { pattern: /\bcurl\s+.*\|\s*(bash|sh)\b/i, reason: "Pipe-to-shell from network", example: "curl x | bash" },
12
+ { pattern: /\bwget\s+.*\|\s*(bash|sh)\b/i, reason: "Pipe-to-shell from network", example: "wget x | sh" },
13
+ ];
14
+ const SENSITIVE_PATH_PREFIXES = ["/etc/", "/usr/", "/var/", "/bin/", "/sbin/", "/root/", "/boot/"];
15
+ function containsSensitivePath(path) {
16
+ if (typeof path !== "string")
17
+ return false;
18
+ const normalized = path.replace(/^~\/?/, "").replace(/^\.\//, "");
19
+ const full = normalized.startsWith("/") ? normalized : `/${normalized}`;
20
+ return SENSITIVE_PATH_PREFIXES.some((p) => full.startsWith(p) || full.includes("/.ssh/"));
21
+ }
22
+ function getDangerousCommandReason(cmd) {
23
+ if (typeof cmd !== "string")
24
+ return undefined;
25
+ const entry = DANGEROUS_COMMAND_PATTERNS.find((e) => e.pattern.test(cmd));
26
+ return entry?.reason;
27
+ }
28
+ function isDangerousCommand(cmd) {
29
+ return getDangerousCommandReason(cmd) !== undefined;
30
+ }
31
+ /**
32
+ * Classify risk based on built-in rules only.
33
+ */
34
+ export function classifyRiskRules(toolName, params) {
35
+ const normalized = toolName.trim().toLowerCase();
36
+ if (normalized === "exec" || normalized === "process") {
37
+ const cmd = params.command ?? params.cmd ?? params.script ?? "";
38
+ if (isDangerousCommand(cmd)) {
39
+ return { risk: "high", reason: getDangerousCommandReason(cmd) };
40
+ }
41
+ }
42
+ if (normalized === "write" || normalized === "edit" || normalized === "apply_patch") {
43
+ const path = params.path ?? params.target ?? params.filePath ?? "";
44
+ if (containsSensitivePath(path)) {
45
+ return { risk: "high", reason: "Writes to system or sensitive path" };
46
+ }
47
+ }
48
+ return { risk: "medium" };
49
+ }
50
+ /**
51
+ * Return built-in risk patterns for display (e.g. identity_list_risk_patterns).
52
+ */
53
+ export function getRiskPatterns() {
54
+ return {
55
+ commandPatterns: DANGEROUS_COMMAND_PATTERNS.map((p) => ({
56
+ example: p.example,
57
+ reason: p.reason,
58
+ })),
59
+ sensitivePaths: [...SENSITIVE_PATH_PREFIXES, "~/.ssh/"],
60
+ };
61
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Shared risk diagnosis: rules + optional LLM re-evaluation.
3
+ * Used by before-tool-call, identity_risk_check, and /identity risk.
4
+ */
5
+ import type { RiskLevel } from "./classify-risk.js";
6
+ import { type LlmRiskCheckConfig } from "./llm-risk-check.js";
7
+ export type DiagnoseRiskResult = {
8
+ risk: RiskLevel;
9
+ reason?: string;
10
+ source: "rules" | "llm";
11
+ };
12
+ export type AuthzLlmConfig = LlmRiskCheckConfig | undefined;
13
+ /**
14
+ * Diagnose risk for a tool call. Uses rules first; when rules return "medium"
15
+ * and LLM config is present, re-evaluates via LLM for context.
16
+ */
17
+ export declare function diagnoseRisk(toolName: string, params: Record<string, unknown>, llmConfig: AuthzLlmConfig, logger?: {
18
+ debug?: (msg: string) => void;
19
+ warn?: (msg: string) => void;
20
+ }): Promise<DiagnoseRiskResult>;
21
+ //# sourceMappingURL=diagnose-risk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose-risk.d.ts","sourceRoot":"","sources":["../../../src/risk/diagnose-risk.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,GAAG,KAAK,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG,SAAS,CAAC;AAE5D;;;GAGG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,cAAc,EACzB,MAAM,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACvE,OAAO,CAAC,kBAAkB,CAAC,CAmC7B"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared risk diagnosis: rules + optional LLM re-evaluation.
3
+ * Used by before-tool-call, identity_risk_check, and /identity risk.
4
+ */
5
+ import { classifyRiskRules } from "./classify-risk.js";
6
+ import { evaluateRiskLlm, } from "./llm-risk-check.js";
7
+ /**
8
+ * Diagnose risk for a tool call. Uses rules first; when rules return "medium"
9
+ * and LLM config is present, re-evaluates via LLM for context.
10
+ */
11
+ export async function diagnoseRisk(toolName, params, llmConfig, logger) {
12
+ const ruleResult = classifyRiskRules(toolName, params);
13
+ if (ruleResult.risk === "medium" &&
14
+ llmConfig?.endpoint &&
15
+ llmConfig?.model) {
16
+ const llmResult = await evaluateRiskLlm(toolName, params, {
17
+ endpoint: llmConfig.endpoint,
18
+ api: llmConfig.api ?? "ollama",
19
+ model: llmConfig.model,
20
+ apiKey: llmConfig.apiKey,
21
+ timeoutMs: llmConfig.timeoutMs ?? 10_000,
22
+ cacheTtlMs: llmConfig.cacheTtlMs ?? 300_000,
23
+ }, logger);
24
+ if (llmResult) {
25
+ return {
26
+ risk: llmResult.risk,
27
+ reason: llmResult.reason,
28
+ source: "llm",
29
+ };
30
+ }
31
+ }
32
+ return {
33
+ risk: ruleResult.risk,
34
+ reason: ruleResult.reason,
35
+ source: "rules",
36
+ };
37
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * LLM-based risk classification for tool calls.
3
+ * Supports Ollama and OpenAI-compatible providers. No core OpenClaw changes.
4
+ * Reference: GuardSpine plugin (ollamaGenerate, runCouncilReview).
5
+ */
6
+ import type { RiskLevel } from "./classify-risk.js";
7
+ export type LlmRiskCheckConfig = {
8
+ endpoint: string;
9
+ api?: "ollama" | "openai-completions";
10
+ model: string;
11
+ apiKey?: string;
12
+ timeoutMs?: number;
13
+ /** Cache TTL ms. 0 = disabled. Default 300000. */
14
+ cacheTtlMs?: number;
15
+ };
16
+ export type LlmRiskResult = {
17
+ risk: RiskLevel;
18
+ reason?: string;
19
+ };
20
+ /**
21
+ * Run LLM risk evaluation. Returns risk level and optional reason, or null on error.
22
+ */
23
+ export declare function evaluateRiskLlm(toolName: string, params: Record<string, unknown>, config: LlmRiskCheckConfig, logger?: {
24
+ debug?: (msg: string) => void;
25
+ warn?: (msg: string) => void;
26
+ }): Promise<LlmRiskResult | null>;
27
+ //# sourceMappingURL=llm-risk-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-risk-check.d.ts","sourceRoot":"","sources":["../../../src/risk/llm-risk-check.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAkDpD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,QAAQ,GAAG,oBAAoB,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA0CF,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAyJjE;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GACvE,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAmE/B"}