@wowok/agent-mcp 2.3.18 → 2.4.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.
Files changed (65) hide show
  1. package/dist/customer/industry-risks.js +31 -31
  2. package/dist/customer/order-monitor.js +57 -57
  3. package/dist/customer/order-strategy.js +89 -89
  4. package/dist/customer/post-purchase.js +97 -97
  5. package/dist/customer/reminder-system.js +26 -26
  6. package/dist/customer/risk-assessment.js +46 -46
  7. package/dist/customer/types.d.ts +4 -4
  8. package/dist/customer/types.js +4 -4
  9. package/dist/customer/user-preferences.js +50 -50
  10. package/dist/experience/intent-distill.js +6 -6
  11. package/dist/knowledge/acquisition-flywheel.js +64 -64
  12. package/dist/knowledge/arbitration-trust.js +10 -10
  13. package/dist/knowledge/demand-matching.js +8 -8
  14. package/dist/knowledge/dynamic-pricing.js +18 -18
  15. package/dist/knowledge/flywheel-loop.js +11 -11
  16. package/dist/knowledge/glossary.js +22 -22
  17. package/dist/knowledge/guard-ledger.d.ts +1 -1
  18. package/dist/knowledge/guard-ledger.js +131 -104
  19. package/dist/knowledge/guard-lint.d.ts +76 -0
  20. package/dist/knowledge/guard-lint.js +590 -0
  21. package/dist/knowledge/guard-puzzle.d.ts +109 -14
  22. package/dist/knowledge/guard-puzzle.js +627 -101
  23. package/dist/knowledge/guard-risk.d.ts +8 -1
  24. package/dist/knowledge/guard-risk.js +868 -296
  25. package/dist/knowledge/guard-templates.d.ts +37 -0
  26. package/dist/knowledge/guard-templates.js +329 -0
  27. package/dist/knowledge/guard-translation.d.ts +14 -2
  28. package/dist/knowledge/guard-translation.js +825 -194
  29. package/dist/knowledge/index.d.ts +8 -4
  30. package/dist/knowledge/index.js +9 -5
  31. package/dist/knowledge/industry-evolution.js +29 -29
  32. package/dist/knowledge/industry-generalizer.js +52 -52
  33. package/dist/knowledge/industry-registry.js +20 -33
  34. package/dist/knowledge/intent-metrics.js +67 -67
  35. package/dist/knowledge/process-model.js +80 -80
  36. package/dist/knowledge/reputation-rules.js +9 -9
  37. package/dist/knowledge/reward-templates.js +51 -51
  38. package/dist/knowledge/safety-rules.js +3 -3
  39. package/dist/knowledge/tool-constraints.js +20 -4
  40. package/dist/knowledge/trust-metrics.js +26 -26
  41. package/dist/schema/call/base.js +16 -16
  42. package/dist/schema/call/bridge.d.ts +32 -32
  43. package/dist/schema/call/demand.d.ts +84 -84
  44. package/dist/schema/call/guard.d.ts +153 -0
  45. package/dist/schema/call/guard.js +50 -0
  46. package/dist/schema/call/permission.d.ts +78 -78
  47. package/dist/schema/call/repository.d.ts +22 -22
  48. package/dist/schema/call/semantic.js +228 -24
  49. package/dist/schema/local/wip.d.ts +39 -19
  50. package/dist/schema/local/wip.js +5 -5
  51. package/dist/schema/messenger/index.d.ts +26 -26
  52. package/dist/schema/messenger/index.js +2 -2
  53. package/dist/schema/operations.d.ts +222 -144
  54. package/dist/schema/query/index.d.ts +38 -38
  55. package/dist/schema/trust/index.d.ts +4 -4
  56. package/dist/schema/utils/guard-parser.js +4 -4
  57. package/dist/schema/utils/node-parser.js +14 -14
  58. package/dist/schemas/bridge_operation.output.json +14 -14
  59. package/dist/schemas/index.json +1 -1
  60. package/dist/schemas/messenger_operation.output.json +17 -15
  61. package/dist/schemas/onchain_events.output.json +14 -14
  62. package/dist/schemas/onchain_operations.output.json +14 -14
  63. package/dist/schemas/onchain_table_data.output.json +14 -14
  64. package/dist/schemas/wip_file.output.json +24 -1
  65. package/package.json +2 -2
@@ -1,364 +1,921 @@
1
+ function rootStr(ctx) {
2
+ return JSON.stringify(ctx.root).toLowerCase();
3
+ }
4
+ function hasSignerCheck(ctx) {
5
+ return JSON.stringify(ctx.root).includes('"Signer"');
6
+ }
7
+ function hasClockCheck(ctx) {
8
+ return JSON.stringify(ctx.root).includes('"Clock"');
9
+ }
10
+ function hasGuardSelfRef(ctx) {
11
+ return JSON.stringify(ctx.root).includes('"Guard"');
12
+ }
13
+ function hasWitness(root) {
14
+ const str = JSON.stringify(root);
15
+ return str.includes('"convert_witness"');
16
+ }
17
+ function queriesRepository(ctx) {
18
+ return rootStr(ctx).includes('repository');
19
+ }
20
+ function queriesProgress(ctx) {
21
+ return rootStr(ctx).includes('progress');
22
+ }
23
+ function extractWitnessCodes(root) {
24
+ const codes = [];
25
+ const str = JSON.stringify(root);
26
+ const matches = str.matchAll(/"convert_witness"\s*:\s*(\d+)/g);
27
+ for (const m of matches) {
28
+ const code = parseInt(m[1], 10);
29
+ if (code >= 100 && code <= 108)
30
+ codes.push(code);
31
+ }
32
+ return codes;
33
+ }
1
34
  const RISK_RULES = [
2
35
  {
3
- id: "R-DST-01",
4
- trigger: "Guard 查询 Repository 数据但未验证写入者权限",
36
+ id: "R-C1-01",
37
+ data_source_class: "type1_onchain_constant",
38
+ trigger: "Guard table contains Address constants with b_submission=false; values may change after Guard rebuild",
5
39
  check: (ctx) => {
6
- const rootStr = JSON.stringify(ctx.root).toLowerCase();
7
- if (rootStr.includes('"query"') && rootStr.includes('repository')) {
8
- const hasSignerCheck = rootStr.includes('"signer"') || rootStr.includes('"context"');
9
- if (!hasSignerCheck) {
10
- return {
11
- id: "R-DST-01",
12
- level: "high",
13
- category: "data_source_trust",
14
- title: "Repository 数据源未验证写入者权限",
15
- description: "Guard 查询 Repository 数据,但未验证谁有权写入该 Repository。" +
16
- "恶意写入者可以操纵 Repository 数据来欺骗 Guard",
17
- affected_stakeholders: ["customer", "provider"],
18
- scenario: "数据源操纵:攻击者写入虚假数据使 Guard 通过",
19
- mitigation: "1) Repository write_guard 中限制写入者权限;" +
20
- "2) 在当前 Guard 中添加 context(Signer) 验证写入者身份;" +
21
- "3) 使用多点数据源交叉验证",
22
- };
40
+ const constAddrEntries = ctx.table.filter((e) => !e.b_submission && e.value_type === "Address" && e.value);
41
+ if (constAddrEntries.length === 0)
42
+ return null;
43
+ return {
44
+ id: "R-C1-01",
45
+ data_source_class: "type1_onchain_constant",
46
+ level: "low",
47
+ category: "data_source_trust",
48
+ title: "Constant object addresses may be inconsistent after Guard rebuild",
49
+ description: `Guard table contains ${constAddrEntries.length} Address constants (b_submission=false).` +
50
+ "Guard is immutable, but if a future Guard rebuild is needed (e.g. to fix logic), the new Guard's table constant addresses may differ from the old version." +
51
+ "Objects referencing the old Guard (Machine/Service, etc.) will not auto-update, leading to constant address inconsistency.",
52
+ affected_stakeholders: ["provider"],
53
+ scenario: "Guard iteration: after rebuild, constant addresses change but references still point to the old Guard",
54
+ mitigation: "1) After Guard rebuild, update all references (Machine forward guard, Service buy_guard, etc.);" +
55
+ "2) Use guard2file to export a backup and record constant-address-to-semantics mappings;" +
56
+ "3) Document constant-address changes in the migration notes",
57
+ evidence: `Constant Address entries: ${constAddrEntries.map((e) => `#${e.identifier}=${e.value}`).join(", ")}`,
58
+ };
59
+ },
60
+ },
61
+ {
62
+ id: "R-C1-02",
63
+ data_source_class: "type1_onchain_constant",
64
+ trigger: "Guard table contains EntityRegistrar(0xaab) or EntityLinker(0xaaa) system address constants",
65
+ check: (ctx) => {
66
+ const sysAddrEntries = ctx.table.filter((e) => !e.b_submission &&
67
+ e.value_type === "Address" &&
68
+ typeof e.value === "string" &&
69
+ (e.value.toLowerCase() === "0xaab" || e.value.toLowerCase() === "0xaaa"));
70
+ if (sysAddrEntries.length === 0)
71
+ return null;
72
+ const wrong = sysAddrEntries.find((e) => (e.value === "0xaab" && e.name && !e.name.toLowerCase().includes("registrar")) ||
73
+ (e.value === "0xaaa" && e.name && !e.name.toLowerCase().includes("linker")));
74
+ return {
75
+ id: "R-C1-02",
76
+ data_source_class: "type1_onchain_constant",
77
+ level: wrong ? "medium" : "info",
78
+ category: "data_source_trust",
79
+ title: wrong
80
+ ? "EntityRegistrar/EntityLinker system address name annotation suspected to be wrong"
81
+ : "Guard uses EntityRegistrar/EntityLinker system address constants",
82
+ description: "EntityRegistrar (0xaab) and EntityLinker (0xaaa) are fixed WoWok protocol system addresses." +
83
+ (wrong
84
+ ? "Detected name annotation does not match the address (e.g. 0xaab not annotated as registrar), which may cause semantic misunderstanding."
85
+ : "Address is correct, but ensure the name annotation is clear to avoid later maintenance confusion."),
86
+ affected_stakeholders: ["provider"],
87
+ scenario: "System address misuse: incorrect 0xaab/0xaaa name annotation causes query target confusion",
88
+ mitigation: "1) Confirm 0xaab corresponds to EntityRegistrar (entity registrar);" +
89
+ "2) Confirm 0xaaa corresponds to EntityLinker (entity linker);" +
90
+ "3) The table entry name should clearly indicate the system address purpose",
91
+ evidence: `System address entries: ${sysAddrEntries.map((e) => `#${e.identifier}=${e.value}(${e.name})`).join(", ")}`,
92
+ };
93
+ },
94
+ },
95
+ {
96
+ id: "R-C1-03",
97
+ data_source_class: "type1_onchain_constant",
98
+ trigger: "Guard queries Repository data without verifying writer permissions",
99
+ check: (ctx) => {
100
+ if (!queriesRepository(ctx))
101
+ return null;
102
+ if (hasSignerCheck(ctx))
103
+ return null;
104
+ return {
105
+ id: "R-C1-03",
106
+ data_source_class: "type1_onchain_constant",
107
+ level: "high",
108
+ category: "data_source_trust",
109
+ title: "Repository data source does not verify writer permissions",
110
+ description: "Guard queries Repository data but does not verify who has permission to write to that Repository." +
111
+ "A malicious writer can manipulate Repository data to deceive the Guard. As a Type1 constant object, " +
112
+ "the Repository's address is immutable, but its internal data can be modified by authorized writers.",
113
+ affected_stakeholders: ["customer", "provider"],
114
+ scenario: "Data source manipulation: attacker writes false data so Guard passes",
115
+ mitigation: "1) Restrict writer permissions in the Repository write_guard;" +
116
+ "2) Add context(Signer) in the current Guard to verify writer identity;" +
117
+ "3) Use multi-point data source cross-validation",
118
+ };
119
+ },
120
+ },
121
+ {
122
+ id: "R-C2-01",
123
+ data_source_class: "type2_witness_derived",
124
+ trigger: "Guard uses multi-hop witness (106/107/108: Arb→Progress/Machine/Service) to derive target object",
125
+ check: (ctx) => {
126
+ const codes = extractWitnessCodes(ctx.root);
127
+ const multiHop = codes.filter((c) => [106, 107, 108].includes(c));
128
+ if (multiHop.length === 0)
129
+ return null;
130
+ const witnessNames = {
131
+ 106: "Arb→Progress (two hops: arb.order → order.progress)",
132
+ 107: "Arb→Machine (two hops: arb.order → order.machine)",
133
+ 108: "Arb→Service (two hops: arb.order → order.service)",
134
+ };
135
+ return {
136
+ id: "R-C2-01",
137
+ data_source_class: "type2_witness_derived",
138
+ level: "medium",
139
+ category: "data_source_trust",
140
+ title: "Multi-hop witness chain derivation may break",
141
+ description: `Guard uses multi-hop witness [${multiHop.join(", ")}] to derive target object.` +
142
+ "Multi-hop witness (e.g. Arb→Progress requires reading arb.order then order.progress) requires intermediate objects to exist and fields to be set." +
143
+ "If an intermediate object does not exist (e.g. Arb's order field is empty) or the relationship chain breaks, witness derivation fails." +
144
+ "In the WoWok protocol this usually returns empty or 0, potentially causing unexpected Guard behavior.\n" +
145
+ multiHop.map((c) => ` - ${c}: ${witnessNames[c]}`).join("\n"),
146
+ affected_stakeholders: ["customer", "provider", "arbitrator"],
147
+ scenario: "Witness break: intermediate object missing causes derivation failure and unexpected Guard behavior",
148
+ mitigation: "1) Confirm the Arb object's order field is properly set (Arb is bound to Order at creation);" +
149
+ "2) Add null checks for derived results in Guard logic;" +
150
+ "3) Prefer single-hop witness (100-105) to reduce derivation chain length;" +
151
+ "4) Test the edge case where Arb is not associated with an Order",
152
+ evidence: `Multi-hop witness codes: ${multiHop.join(", ")}`,
153
+ };
154
+ },
155
+ },
156
+ {
157
+ id: "R-C2-02",
158
+ data_source_class: "type2_witness_derived",
159
+ trigger: "Guard uses witness but source object's object_type may not match the witness source type",
160
+ check: (ctx) => {
161
+ const codes = extractWitnessCodes(ctx.root);
162
+ if (codes.length === 0)
163
+ return null;
164
+ const witnessSourceTypes = {
165
+ 100: "Order", 101: "Order", 102: "Order",
166
+ 103: "Progress",
167
+ 104: "Arb", 105: "Arb", 106: "Arb", 107: "Arb", 108: "Arb",
168
+ };
169
+ const rootStr = JSON.stringify(ctx.root);
170
+ const mismatches = [];
171
+ for (const code of codes) {
172
+ const expectedSource = witnessSourceTypes[code];
173
+ if (!expectedSource)
174
+ continue;
175
+ const pattern1 = new RegExp(`"identifier"\\s*:\\s*(\\d+)\\s*,\\s*"convert_witness"\\s*:\\s*${code}`, "g");
176
+ const pattern2 = new RegExp(`"convert_witness"\\s*:\\s*${code}\\s*,\\s*"identifier"\\s*:\\s*(\\d+)`, "g");
177
+ const foundIds = new Set();
178
+ let m;
179
+ while ((m = pattern1.exec(rootStr)) !== null) {
180
+ foundIds.add(parseInt(m[1], 10));
181
+ }
182
+ while ((m = pattern2.exec(rootStr)) !== null) {
183
+ foundIds.add(parseInt(m[1], 10));
184
+ }
185
+ for (const id of foundIds) {
186
+ const entry = ctx.table.find((e) => e.identifier === id);
187
+ if (entry?.object_type && entry.object_type !== expectedSource) {
188
+ mismatches.push(`witness ${code}(${expectedSource}→?) references #${id} object_type=${entry.object_type}`);
189
+ }
23
190
  }
24
191
  }
25
- return null;
192
+ if (mismatches.length === 0)
193
+ return null;
194
+ return {
195
+ id: "R-C2-02",
196
+ data_source_class: "type2_witness_derived",
197
+ level: "low",
198
+ category: "data_source_trust",
199
+ title: "witness source object object_type may not match the witness source type",
200
+ description: "Guard uses witness derivation, but the source object's object_type in table is inconsistent with the witness expected source type.\n" +
201
+ mismatches.map((mm) => ` - ${mm}`).join("\n") +
202
+ "\nNote: object_type is an auxiliary annotation field on table entries; it is not strictly validated at on-chain creation." +
203
+ "However, wrong annotations cause semantic misunderstanding and maintenance difficulty.",
204
+ affected_stakeholders: ["provider"],
205
+ scenario: "Type annotation mismatch: witness source type annotation is wrong, causing semantic confusion",
206
+ mitigation: "1) Fix the table entry's object_type field to match the witness source type;" +
207
+ "2) witness 100-102 source type is Order; 103 is Progress; 104-108 is Arb;" +
208
+ "3) Use wowok_buildin_info(info='guard instructions') to query the full witness definition",
209
+ evidence: mismatches.join("; "),
210
+ };
26
211
  },
27
212
  },
28
213
  {
29
- id: "R-DST-02",
30
- trigger: "Guard 查询 Progress 状态但未验证 Machine 发布状态",
214
+ id: "R-C2-03",
215
+ data_source_class: "type2_witness_derived",
216
+ trigger: "Guard queries Progress status without verifying Machine publish state",
31
217
  check: (ctx) => {
32
- const rootStr = JSON.stringify(ctx.root).toLowerCase();
33
- if (rootStr.includes('progress') && ctx.scene?.host_object === "Machine") {
34
- return {
35
- id: "R-DST-02",
36
- level: "medium",
37
- category: "data_source_trust",
38
- title: "Progress 数据依赖 Machine 不可变性",
39
- description: "Guard 查询 Progress 状态。如果 Machine 未发布,Provider 可以修改 Machine 节点结构," +
40
- "导致 Guard 验证的节点名失效或被重命名。",
41
- affected_stakeholders: ["customer"],
42
- scenario: "Provider 修改 Machine 节点名使 Guard 验证失效",
43
- mitigation: "1) 确保 Machine 已发布(bPublished=true)再绑定 Guard;" +
44
- "2) Guard 中查询 Machine.bPublished 字段验证;" +
45
- "3) 使用节点 ID 而非节点名进行比较",
46
- };
47
- }
48
- return null;
218
+ if (!queriesProgress(ctx))
219
+ return null;
220
+ if (!hasWitness(ctx.root))
221
+ return null;
222
+ if (ctx.scene?.host_object !== "Machine")
223
+ return null;
224
+ return {
225
+ id: "R-C2-03",
226
+ data_source_class: "type2_witness_derived",
227
+ level: "medium",
228
+ category: "data_source_trust",
229
+ title: "Progress data depends on Machine immutability",
230
+ description: "Guard queries Progress status via witness, and the Guard is bound to a Machine." +
231
+ "If the Machine is not published (bPublished=false), the Provider can modify the Machine node structure, " +
232
+ "causing node names validated by the Guard to become invalid or be renamed. Progress's current node name, node time, etc. " +
233
+ "all depend on the Machine's node definitions.",
234
+ affected_stakeholders: ["customer"],
235
+ scenario: "Provider modifies Machine node names to invalidate Guard verification",
236
+ mitigation: "1) Ensure the Machine is published (bPublished=true) before binding the Guard;" +
237
+ "2) Query the Machine.bPublished field in the Guard to verify;" +
238
+ "3) Use node IDs instead of node names for comparison",
239
+ };
49
240
  },
50
241
  },
51
242
  {
52
- id: "R-SF-01",
53
- trigger: "Guard 依赖提交数据但未验证签名者身份(无 Signer 检查=high;有 Signer 检查=info 降级)",
243
+ id: "R-C2-04",
244
+ data_source_class: "type2_witness_derived",
245
+ trigger: "Guard uses witness query and the query parameter requires type conversion (e.g. Address → U64 timestamp key)",
246
+ check: (ctx) => {
247
+ if (!hasWitness(ctx.root))
248
+ return null;
249
+ const str = rootStr(ctx);
250
+ const hasTypeConversion = str.includes("convert_number_address") ||
251
+ str.includes("convert_address_number") ||
252
+ str.includes("convert_u256") ||
253
+ str.includes("convert_string");
254
+ const queriesRepo = str.includes("repository") && str.includes('"query"');
255
+ const queriesProgressHistory = str.includes("progress_history") && str.includes('"query"');
256
+ if (!queriesRepo && !queriesProgressHistory)
257
+ return null;
258
+ return {
259
+ id: "R-C2-04",
260
+ data_source_class: "type2_witness_derived",
261
+ level: "medium",
262
+ category: "data_source_trust",
263
+ title: "witness query parameter requires type translation (missing will cause type mismatch or wrong comparison)",
264
+ description: "Guard queries object fields via witness, but the query parameter type may not match the type declared in table.\n" +
265
+ "Typical scenarios:\n" +
266
+ " - Repository.data query: the key may be of Address type (vector<u8> address representation in Move), " +
267
+ "but the actual semantics is a U64 timestamp, requiring convert_number_address translation\n" +
268
+ " - Progress.history query: Forward ID may need to be converted from String to Address\n" +
269
+ " - Order field query: some ID fields may be stored as Address but need U256 for comparison\n" +
270
+ (hasTypeConversion
271
+ ? "\nThe current Guard already uses type conversion nodes, but ensure the conversion direction is correct."
272
+ : "\nWarning: no type conversion node detected in the current Guard; parameter type mismatch risk may exist."),
273
+ affected_stakeholders: ["provider", "customer"],
274
+ scenario: "Query parameter type does not match the parameters type defined by GUARDQUERY, causing creation failure or runtime comparison error",
275
+ mitigation: "1) Query GUARDQUERY to confirm the parameters type and return type of each query instruction;\n" +
276
+ "2) If table declares Address but the query needs U64, use convert_number_address to translate;\n" +
277
+ "3) Use wowok_buildin_info info='guard instructions' to verify parameter types;\n" +
278
+ "4) Verify type compatibility via schema_query before creation",
279
+ evidence: hasTypeConversion ? "Type conversion node in use" : "No type conversion node detected",
280
+ };
281
+ },
282
+ },
283
+ {
284
+ id: "R-C3-01",
285
+ data_source_class: "type3_submitted_object",
286
+ trigger: "Guard depends on submitted data but does not verify signer identity (no Signer check=high; with Signer check=info downgrade)",
54
287
  check: (ctx) => {
55
288
  const hasSubmission = ctx.table.some((e) => e.b_submission);
56
289
  if (!hasSubmission)
57
290
  return null;
58
291
  if (ctx.scene?.binding_field === "voting_guard")
59
292
  return null;
60
- const rootStr = JSON.stringify(ctx.root);
61
- const hasSignerCheck = rootStr.includes('"Signer"');
62
- if (!hasSignerCheck) {
293
+ if (!hasSignerCheck(ctx)) {
294
+ if (ctx.scene?.id === "machine_forward_guard") {
295
+ return {
296
+ id: "R-C3-01",
297
+ data_source_class: "type3_submitted_object",
298
+ level: "medium",
299
+ category: "submission_forgery",
300
+ title: "Submitted data not bound to signer identity (Forward scenario implicitly verified by permissionIndex)",
301
+ description: "Guard accepts runtime-submitted data (b_submission=true), and root does not explicitly verify Signer." +
302
+ "This Guard is used for Machine Forward; Forward's permissionIndex/namedOperator already implicitly verifies operator identity, " +
303
+ "so the Signer binding risk is reduced. But permissionIndex only verifies 'operator has permission', " +
304
+ "not 'submitted data belongs to operator' — for example, a merchant holding permissionIndex can still submit someone else's credentials.",
305
+ affected_stakeholders: ["customer", "provider"],
306
+ scenario: "In-permission forgery: an authorized operator submits someone else's data (e.g. someone else's Merkle Root)",
307
+ mitigation: "1) If the submitted data should belong to the operator (e.g. the operator's address), it is still recommended to add context(Signer) binding;" +
308
+ "2) If the submitted data is a public credential (e.g. Merkle Root string), the current risk is acceptable;" +
309
+ "3) Use retained_submission to retain submitted data for later audit",
310
+ };
311
+ }
63
312
  return {
64
- id: "R-SF-01",
313
+ id: "R-C3-01",
314
+ data_source_class: "type3_submitted_object",
65
315
  level: "high",
66
316
  category: "submission_forgery",
67
- title: "提交数据未与签名者身份绑定",
68
- description: "Guard 接受运行时提交数据,但未验证提交者是否为当前交易签名者。" +
69
- "攻击者可以提交他人的数据(如他人的地址、他人的凭证)来通过 Guard",
317
+ title: "Submitted data not bound to signer identity",
318
+ description: "Guard accepts runtime-submitted data (b_submission=true), but does not verify whether the submitter is the current transaction signer." +
319
+ "An attacker can submit someone else's data (e.g. someone else's address, someone else's credentials) to pass the Guard." +
320
+ "Type3 submitted objects are isomorphic with Type1 at the native layer (TYPE_QUERY+TYPE_CONSTANT), " +
321
+ "the only difference being that the value is injected by submission, so identity must be bound via logical constraints.",
70
322
  affected_stakeholders: ["customer", "provider"],
71
- scenario: "身份伪造:攻击者提交他人的地址冒充授权用户",
72
- mitigation: "1) 添加 context(Signer) 与提交数据绑定验证;" +
73
- "2) 使用 vec_contains_address 验证签名者在白名单中;" +
74
- "3) 使用 Passport 凭证替代裸地址提交",
323
+ scenario: "Identity forgery: attacker submits someone else's address to impersonate an authorized user",
324
+ mitigation: "1) Add context(Signer) binding with the submitted data for verification;" +
325
+ "2) Use vec_contains_address to verify the signer is in the whitelist;" +
326
+ "3) Use Passport credentials instead of bare address submission",
75
327
  };
76
328
  }
77
329
  return {
78
- id: "R-SF-01",
330
+ id: "R-C3-01",
331
+ data_source_class: "type3_submitted_object",
79
332
  level: "info",
80
333
  category: "submission_forgery",
81
- title: "提交数据已通过 context(Signer) 间接绑定(请确认绑定方向正确)",
82
- description: "Guard 接受运行时提交数据,且 root 子树中包含 context(Signer) 检查," +
83
- "提交数据与签名者形成间接绑定,伪造风险已降低。" +
84
- "但需人工确认:context(Signer) 比较的常量是正确角色(如 Provider 地址而非 Customer 地址)," +
85
- "且提交数据确实应由该签名者提供。",
334
+ title: "Submitted data is indirectly bound via context(Signer) (confirm the binding direction is correct)",
335
+ description: "Guard accepts runtime-submitted data, and the root subtree contains a context(Signer) check, " +
336
+ "forming an indirect binding between submitted data and signer, reducing forgery risk." +
337
+ "But manual confirmation is needed: the constant compared with context(Signer) is the correct role (e.g. Provider address rather than Customer address), " +
338
+ "and the submitted data should indeed be provided by that signer. See R-C4-01 Signer direction risk.",
86
339
  affected_stakeholders: ["customer", "provider"],
87
- scenario: "间接绑定待确认:Signer 检查方向与提交数据归属是否匹配",
88
- mitigation: "1) 确认 context(Signer) 与正确的 table 常量比较(Provider 地址而非 Customer 地址);" +
89
- "2) 确认提交数据的来源角色与 Signer 检查一致;" +
90
- "3) 如需更严格绑定,使用 query 验证提交者链上身份",
340
+ scenario: "Indirect binding to confirm: whether the Signer check direction matches the submitted data ownership",
341
+ mitigation: "1) Confirm context(Signer) is compared with the correct table constant (Provider address rather than Customer address);" +
342
+ "2) Confirm the source role of the submitted data matches the Signer check;" +
343
+ "3) For stricter binding, use query to verify the submitter's on-chain identity",
91
344
  };
92
345
  },
93
346
  },
94
347
  {
95
- id: "R-SF-02",
96
- trigger: "voting_guard 权重来自提交数据但未验证来源",
348
+ id: "R-C3-02",
349
+ data_source_class: "type3_submitted_object",
350
+ trigger: "voting_guard weight comes from submitted data without verifying source",
97
351
  check: (ctx) => {
98
- if (ctx.scene?.binding_field === "voting_guard") {
99
- const numericEntries = ctx.table.filter((e) => e.b_submission && ["U8", "U16", "U32", "U64", "U128", "U256"].includes(e.value_type));
100
- if (numericEntries.length > 0) {
101
- return {
102
- id: "R-SF-02",
103
- level: "critical",
104
- category: "submission_forgery",
105
- title: "投票权重来自用户提交,可能被伪造",
106
- description: "voting_guard 的权重数据来自 b_submission=true 的 table 条目。" +
107
- "如果未通过 EntityRegistrar 或其他可信来源验证权重," +
108
- "投票者可以提交任意高权重操纵投票结果。",
109
- affected_stakeholders: ["arbitrator"],
110
- scenario: "权重伪造:投票者提交虚假高分操纵仲裁结果",
111
- mitigation: "1) 权重应从 EntityRegistrar 查询而非用户提交;" +
112
- "2) 使用 query(entity.records) 获取链上注册的声誉分;" +
113
- "3) Guard 中验证提交权重与链上记录一致",
114
- };
115
- }
116
- }
117
- return null;
352
+ if (ctx.scene?.binding_field !== "voting_guard")
353
+ return null;
354
+ const numericEntries = ctx.table.filter((e) => e.b_submission &&
355
+ ["U8", "U16", "U32", "U64", "U128", "U256"].includes(e.value_type));
356
+ if (numericEntries.length === 0)
357
+ return null;
358
+ return {
359
+ id: "R-C3-02",
360
+ data_source_class: "type3_submitted_object",
361
+ level: "critical",
362
+ category: "submission_forgery",
363
+ title: "Voting weight comes from user submission and may be forged",
364
+ description: "voting_guard weight data comes from table entries with b_submission=true (Type3 submitted values)." +
365
+ "If the weight is not verified through EntityRegistrar or other trusted sources, " +
366
+ "a voter can submit arbitrarily high weights to manipulate the voting result." +
367
+ "Type3 submitted values are only validated by type byte (value[0]) at the native layer; value range and source are not checked.",
368
+ affected_stakeholders: ["arbitrator"],
369
+ scenario: "Weight forgery: voters submit fake high scores to manipulate the arbitration result",
370
+ mitigation: "1) Weights should be queried from EntityRegistrar rather than submitted by users;" +
371
+ "2) Use query(entity.records) to obtain on-chain registered reputation scores;" +
372
+ "3) Verify in the Guard that the submitted weight matches the on-chain record",
373
+ evidence: `Submitted numeric entries: ${numericEntries.map((e) => `#${e.identifier}(${e.value_type})`).join(", ")}`,
374
+ };
118
375
  },
119
376
  },
120
377
  {
121
- id: "R-GT-01",
122
- trigger: "分账 Guard 可能导致某些参与者被跳过",
378
+ id: "R-C3-03",
379
+ data_source_class: "type3_submitted_object",
380
+ trigger: "Guard contains Address entries with b_submission=true; the runtime submitted object type may not match expectations",
123
381
  check: (ctx) => {
124
- if (ctx.scene?.binding_field === "order_allocators") {
125
- return {
126
- id: "R-GT-01",
127
- level: "medium",
128
- category: "game_theory",
129
- title: "order_allocators first-match-wins 可能导致分账不公",
130
- description: "order_allocators 采用 first-match-wins 策略:第一个 Guard 通过的 Allocator 执行分账。" +
131
- "如果顺序设计不当,某些参与者可能永远无法获得分账。" +
132
- "例如:如果第一个 Allocator 的 Guard 条件过宽,后续更精确的 Allocator 永远不会执行。",
133
- affected_stakeholders: ["provider"],
134
- scenario: "分账跳过:Provider 的分账 Allocator 被前面的宽泛条件跳过",
135
- mitigation: "1) 确保 order_allocators 顺序从严格到宽松;" +
136
- "2) 每个 Allocator 的 Guard 条件应互斥;" +
137
- "3) 考虑使用逻辑_or 依赖而非顺序排列",
138
- };
139
- }
140
- return null;
382
+ const submittedAddr = ctx.table.filter((e) => e.b_submission && e.value_type === "Address");
383
+ if (submittedAddr.length === 0)
384
+ return null;
385
+ const noType = submittedAddr.filter((e) => !e.object_type);
386
+ const hasQuery = rootStr(ctx).includes('"query"');
387
+ if (!hasQuery)
388
+ return null;
389
+ return {
390
+ id: "R-C3-03",
391
+ data_source_class: "type3_submitted_object",
392
+ level: noType.length > 0 ? "medium" : "low",
393
+ category: "submission_forgery",
394
+ title: noType.length > 0
395
+ ? "Submitted object missing object_type annotation; runtime type validation relies on native inference"
396
+ : "Submitted object type validation relies on runtime native inference",
397
+ description: "Guard contains " + submittedAddr.length + " Address entries with b_submission=true (Type3 submitted objects)." +
398
+ "The native layer (passport.rs#parse_table_items) only checks the value[0] type byte for submission; " +
399
+ "object_type is derived at runtime by wobject_type and does not strictly validate the actual submitted object type.\n" +
400
+ (noType.length > 0
401
+ ? `Warning: ${noType.length} entries are missing object_type annotation, semantics are unclear.`
402
+ : "object_type is annotated, but still confirm the annotation matches what the query expects."),
403
+ affected_stakeholders: ["customer", "provider"],
404
+ scenario: "Type mismatch: submitting a wrong-typed object causes query to return unexpected values",
405
+ mitigation: "1) Annotate object_type for all Address entries with b_submission=true;" +
406
+ "2) object_type should match the object type expected by the query instruction;" +
407
+ "3) Indirectly verify the object type via query results in Guard logic (e.g. query order.amount to verify it is an Order)",
408
+ evidence: noType.length > 0
409
+ ? `Missing object_type: ${noType.map((e) => `#${e.identifier}`).join(", ")}`
410
+ : `Annotated: ${submittedAddr.map((e) => `#${e.identifier}=${e.object_type}`).join(", ")}`,
411
+ };
141
412
  },
142
413
  },
143
414
  {
144
- id: "R-GT-02",
145
- trigger: "流程 Guard 时间锁可能锁定弱势方",
415
+ id: "R-C3-04",
416
+ data_source_class: "type3_submitted_object",
417
+ trigger: "Repository write_guard does not verify id_from_submission type",
146
418
  check: (ctx) => {
147
- const rootStr = JSON.stringify(ctx.root);
148
- if (rootStr.includes("Clock") && rootStr.includes("calc_number_add")) {
149
- return {
150
- id: "R-GT-02",
151
- level: "medium",
152
- category: "game_theory",
153
- title: "时间锁可能锁定处于弱势的一方",
154
- description: "Guard 使用时间锁(context(Clock) + calc_number_add)。时间锁本身是中性的," +
155
- "但需检查锁定方向:如果锁定 Customer 先付款但不能及时验收,Provider 获得优势;" +
156
- "如果锁定 Provider 先交付但不能及时收款,Customer 获得优势。",
157
- affected_stakeholders: ["customer", "provider"],
158
- scenario: "时间不对等:一方被时间锁定,另一方有行动自由",
159
- mitigation: "1) 确保时间锁对称(双方都有时间约束);" +
160
- "2) 时间锁到期后提供退出机制(如自动退款/自动完成);" +
161
- "3) Machine 中设计对应的超时 Forward 节点",
162
- };
163
- }
164
- return null;
419
+ if (ctx.scene?.binding_field !== "write_guard")
420
+ return null;
421
+ return {
422
+ id: "R-C3-04",
423
+ data_source_class: "type3_submitted_object",
424
+ level: "high",
425
+ category: "binding_risk",
426
+ title: "Repository write_guard must verify id_from_submission type",
427
+ description: "The table entry referenced by Repository write_guard's id_from_submission must be of Address type (Type3 submitted object)." +
428
+ "The entry referenced by data_from_submission must match the Repository value_type." +
429
+ "Type mismatch will cause write failure or data corruption. The write_guard submission contains both id and data parts; " +
430
+ "the native layer distinguishes them by op_code, but value types must match the table declaration.",
431
+ affected_stakeholders: ["provider"],
432
+ scenario: "Type mismatch: write failure or data corruption",
433
+ mitigation: "1) Confirm the entry referenced by id_from_submission has value_type=Address;" +
434
+ "2) Confirm the entry referenced by data_from_submission has value_type matching the Repository;" +
435
+ "3) Cover write scenarios in gen_passport tests",
436
+ };
165
437
  },
166
438
  },
167
439
  {
168
- id: "R-GT-03",
169
- trigger: "Arbitration usage_guard 可能阻止合法争议",
440
+ id: "R-C4-01",
441
+ data_source_class: "type4_system_context",
442
+ trigger: "Guard contains context(Signer) check; need to confirm comparison direction (== vs !=) and compared object role",
170
443
  check: (ctx) => {
171
- if (ctx.scene?.binding_field === "usage_guard") {
172
- const rootStr = JSON.stringify(ctx.root);
173
- const andCount = (rootStr.match(/"logic_and"/g) || []).length;
174
- if (andCount >= 3) {
175
- return {
176
- id: "R-GT-03",
177
- level: "high",
178
- category: "game_theory",
179
- title: "usage_guard 条件过多可能阻止合法争议",
180
- description: `检测到 ${andCount} 个 logic_and 组合。过多 AND 条件使争议门槛过高,` +
181
- "Customer 可能无法发起合法争议,Provider 获得不正当优势。",
182
- affected_stakeholders: ["customer"],
183
- scenario: "争议门槛过高:Customer 无法发起合法争议",
184
- mitigation: "1) 重新评估每个 AND 条件的必要性;" +
185
- "2) 考虑使用 logic_or 降低门槛;" +
186
- "3) 将非必要条件移到 voting_guard 而非 usage_guard",
187
- };
188
- }
189
- }
190
- return null;
444
+ if (!hasSignerCheck(ctx))
445
+ return null;
446
+ const rootStrRaw = JSON.stringify(ctx.root);
447
+ const hasEqual = rootStrRaw.includes('"logic_equal"');
448
+ const hasNotEqual = rootStrRaw.includes('"logic_not_equal"');
449
+ return {
450
+ id: "R-C4-01",
451
+ data_source_class: "type4_system_context",
452
+ level: "medium",
453
+ category: "system_context_risk",
454
+ title: "Signer check direction needs manual confirmation (comparison operator and role match)",
455
+ description: "Guard contains a context(Signer) check. Signer is a Type4 system context, " +
456
+ "from TransactionContext.sender() (transaction initiator address).\n" +
457
+ `Detected comparison: ${hasEqual ? "logic_equal(==)" : ""} ${hasNotEqual ? "logic_not_equal(!=)" : ""}\n` +
458
+ "Manual confirmation required:\n" +
459
+ " 1) Whether the comparison direction is correct: use == for identity verification (Signer == authorized address), use != for exclusion (Signer != blacklist address)\n" +
460
+ " 2) Whether the compared object role is correct: buy_guard should compare the Customer address, Forward.guard may compare the Provider address\n" +
461
+ " 3) Wrong direction causes reverse effect: == blacklist address would only allow blacklisted users",
462
+ affected_stakeholders: ["customer", "provider"],
463
+ scenario: "Wrong direction: Signer == blacklist address would only allow blacklisted users",
464
+ mitigation: "1) Confirm logic_equal is used for identity verification (Signer == authorized address);" +
465
+ "2) Confirm logic_not_equal is used for exclusion (Signer != blacklist address);" +
466
+ "3) Confirm the compared table constant is the address of the correct role;" +
467
+ "4) Cover both expected-pass and expected-fail scenarios in gen_passport tests",
468
+ evidence: `Comparison: ${hasEqual ? "==" : ""} ${hasNotEqual ? "!=" : ""}`,
469
+ };
191
470
  },
192
471
  },
193
472
  {
194
- id: "R-LG-01",
195
- trigger: "Guard root 为 identifier 直接返回 Bool",
473
+ id: "R-C4-02",
474
+ data_source_class: "type4_system_context",
475
+ trigger: "Guard uses context(Clock) for precise boundary time-lock comparison",
196
476
  check: (ctx) => {
197
- if (ctx.root?.type === "identifier") {
198
- return {
199
- id: "R-LG-01",
200
- level: "medium",
201
- category: "logic_gap",
202
- title: "Root 直接返回提交的 Bool 值,可被操纵",
203
- description: "Guard root 是 identifier 节点,直接返回 table 中的 Bool 值。" +
204
- "如果该条目 b_submission=true,调用者可以直接提交 true 绕过所有验证。",
205
- affected_stakeholders: ["customer", "provider"],
206
- scenario: "绕过验证:调用者提交 true 直接通过 Guard",
207
- mitigation: "1) 确保 identifier 引用的条目 b_submission=false(常量);" +
208
- "2) 或添加额外的 logic_and 条件进行交叉验证",
209
- };
210
- }
211
- return null;
477
+ if (!hasClockCheck(ctx))
478
+ return null;
479
+ const str = rootStr(ctx);
480
+ const hasGreaterOrEqual = str.includes("logic_as_u256_greater_or_equal");
481
+ const hasGreater = str.includes("logic_as_u256_greater");
482
+ if (!hasGreaterOrEqual && !hasGreater)
483
+ return null;
484
+ return {
485
+ id: "R-C4-02",
486
+ data_source_class: "type4_system_context",
487
+ level: "low",
488
+ category: "system_context_risk",
489
+ title: "Clock time-lock should have tolerance to avoid boundary invalidation from validator block-time variance",
490
+ description: "Guard uses context(Clock) for time-lock comparison. Clock comes from the timestamp_ms field (U64 millisecond timestamp) of system shared object 0x6.\n" +
491
+ "The Sui on-chain timestamp is produced by validator consensus and has a slight deviation from real time (usually < 1 second), " +
492
+ "and block-time variance may cause timestamp jumps. Using precise boundaries (>= or ==) may cause:\n" +
493
+ " - The time-lock expiration transaction is packaged exactly at the moment but fails due to slight timestamp deviation\n" +
494
+ " - Or conversely, slight deviation causes the time-lock to pass early\n" +
495
+ "It is recommended to use > instead of >=, or add a small tolerance constant.",
496
+ affected_stakeholders: ["customer", "provider"],
497
+ scenario: "Timestamp variance: validator block-time deviation causes boundary transactions to fail or pass early",
498
+ mitigation: "1) Use logic_as_u256_greater (>) instead of greater_or_equal (>=) for expiration judgment;" +
499
+ "2) Or add a small tolerance constant (e.g. 1000ms) in calc_number_add;" +
500
+ "3) Time-locks should not rely on second-level precision; reserve minute-level tolerance;" +
501
+ "4) Design timeout Forward nodes in the Machine as a fallback",
502
+ evidence: `Comparison: ${hasGreaterOrEqual ? ">=" : ""} ${hasGreater ? ">" : ""}`,
503
+ };
212
504
  },
213
505
  },
214
506
  {
215
- id: "R-LG-02",
216
- trigger: "Guard 使用 logic_as_u256_greater 但未处理空值",
507
+ id: "R-C4-03",
508
+ data_source_class: "type4_system_context",
509
+ trigger: "Guard uses context(Guard) self-reference",
217
510
  check: (ctx) => {
218
- const rootStr = JSON.stringify(ctx.root);
219
- if (rootStr.includes("logic_as_u256_greater") && rootStr.includes("query")) {
220
- return {
221
- id: "R-LG-02",
222
- level: "medium",
223
- category: "logic_gap",
224
- title: "数值比较未处理查询返回空值的情况",
225
- description: "Guard 使用 logic_as_u256_greater 进行数值比较,且操作数来自 query。" +
226
- "如果 query 返回空值(如对象不存在、字段未设置),比较结果可能不确定。" +
227
- "WoWok 协议中空值通常被视为 0,可能导致非预期通过。",
228
- affected_stakeholders: ["customer", "provider"],
229
- scenario: "空值误判:query 返回 0 导致 > 0 条件意外通过或失败",
230
- mitigation: "1) 在比较前添加 query 结果存在性检查;" +
231
- "2) 使用 query_reward_record_exists query_progress_history_find 先验证;" +
232
- "3) 考虑添加 logic_and(对象存在, 数值比较) 组合",
233
- };
234
- }
235
- return null;
511
+ if (!hasGuardSelfRef(ctx))
512
+ return null;
513
+ return {
514
+ id: "R-C4-03",
515
+ data_source_class: "type4_system_context",
516
+ level: "info",
517
+ category: "system_context_risk",
518
+ title: "Guard uses context(Guard) self-reference (rare scenario, needs special explanation)",
519
+ description: "Guard contains a context(Guard) node. Guard is a Type4 system context, " +
520
+ "returning the ObjectID of the currently verified Guard object itself.\n" +
521
+ "This is a rare scenario, typically used for:\n" +
522
+ " 1) Guard referencing its own address as a replacement for a table constant (avoiding hardcoding)\n" +
523
+ " 2) Identifying the current Guard in a rely combination (rare)\n" +
524
+ "Need to confirm whether the use of context(Guard) is necessary and whether the comparison object is correct.",
525
+ affected_stakeholders: ["provider"],
526
+ scenario: "Self-reference misuse: context(Guard) comparing wrong object causes logic invalidation",
527
+ mitigation: "1) Confirm the context(Guard) comparison object is the Guard ObjectID rather than an address constant;" +
528
+ "2) Evaluate whether self-reference is truly needed; usually a table constant can replace it;" +
529
+ "3) Verify in gen_passport tests that self-reference behavior meets expectations",
530
+ };
236
531
  },
237
532
  },
238
533
  {
239
- id: "R-LG-03",
240
- trigger: "Guard 使用 logic_string_contains 可能被子串匹配绕过",
534
+ id: "R-X1-01",
535
+ data_source_class: "cross_type",
536
+ trigger: "Time-lock query returns 0 causing logic_as_u256_greater_or_equal comparison to always be true",
241
537
  check: (ctx) => {
242
- const rootStr = JSON.stringify(ctx.root);
243
- if (rootStr.includes("logic_string_contains") && !rootStr.includes("logic_string_nocase")) {
244
- return {
245
- id: "R-LG-03",
246
- level: "low",
247
- category: "logic_gap",
248
- title: "logic_string_contains 子串匹配可能被绕过",
249
- description: "Guard 使用 logic_string_contains 进行字符串匹配。" +
250
- "子串匹配可能被绕过:例如检查 'Completed' 时,'NotCompleted' 也包含 'Completed'。" +
251
- "此外,大小写敏感可能导致非预期失败。",
252
- affected_stakeholders: ["customer", "provider"],
253
- scenario: "子串误匹配:'NotCompleted' 包含 'Completed' 导致误通过",
254
- mitigation: "1) 优先使用 logic_equal 进行精确匹配;" +
255
- "2) 如需子串匹配,添加边界条件(如前缀+后缀);" +
256
- "3) 确认是否需要大小写不敏感(使用 nocase 变体)",
257
- };
258
- }
259
- return null;
538
+ const str = rootStr(ctx);
539
+ const hasGreaterOrEqual = str.includes("logic_as_u256_greater_or_equal");
540
+ const hasClock = str.includes('"clock"');
541
+ const hasCalcAdd = str.includes("calc_number_add");
542
+ const hasQuery = str.includes('"query"');
543
+ if (!(hasGreaterOrEqual && hasClock && hasCalcAdd && hasQuery))
544
+ return null;
545
+ return {
546
+ id: "R-X1-01",
547
+ data_source_class: "cross_type",
548
+ level: "medium",
549
+ category: "logic_gap",
550
+ title: "Time-lock query may return 0 causing >= comparison to always be true",
551
+ description: "Guard uses logic_as_u256_greater_or_equal for time-lock comparison, and the compared baseline value comes from a query (Type1/2/3 data sources)." +
552
+ "If the query returns 0 (e.g. querying a timestamp of an operation that hasn't happened, or unset node entry time), " +
553
+ "then Clock >= 0 + timeout is always true, the time-lock is effectively void, and the disadvantaged party can bypass the time constraint immediately.",
554
+ affected_stakeholders: ["customer", "provider"],
555
+ scenario: "Time-lock invalidation: query returns 0, making the >= condition always pass",
556
+ mitigation: "1) Use a query that returns the current node entry time (e.g. progress.current_time, id=1272) " +
557
+ "instead of the operation timestamp (e.g. progress.session.forward.time, id=1271);" +
558
+ "2) Add a precondition check query > 0 before the time-lock;" +
559
+ "3) Confirm the query must have a value when the Guard executes (query current node attributes rather than historical operations)",
560
+ };
260
561
  },
261
562
  },
262
563
  {
263
- id: "R-LG-04",
264
- trigger: "时间锁 query 返回 0 导致 logic_as_u256_greater_or_equal 比较永远为 true",
564
+ id: "R-X1-02",
565
+ data_source_class: "cross_type",
566
+ trigger: "Guard uses logic_as_u256_greater but does not handle null values",
265
567
  check: (ctx) => {
266
- const rootStr = JSON.stringify(ctx.root);
267
- const hasGreaterOrEqual = rootStr.includes("logic_as_u256_greater_or_equal");
268
- const hasClock = rootStr.includes('"Clock"');
269
- const hasCalcAdd = rootStr.includes("calc_number_add");
270
- const hasQuery = rootStr.includes('"query"');
271
- if (hasGreaterOrEqual && hasClock && hasCalcAdd && hasQuery) {
272
- return {
273
- id: "R-LG-04",
274
- level: "medium",
275
- category: "logic_gap",
276
- title: "时间锁查询可能返回 0 导致 >= 比较永远为 true",
277
- description: "Guard 使用 logic_as_u256_greater_or_equal 进行时间锁比较,且被比较的基准值来自 query。" +
278
- "如果 query 返回 0(如查询尚未发生的操作时间戳、未设置的节点进入时间)," +
279
- " Clock >= 0 + timeout 永远为 true,时间锁形同虚设,弱势方可以立即绕过时间约束。",
280
- affected_stakeholders: ["customer", "provider"],
281
- scenario: "时间锁失效:query 返回 0 使 >= 条件永远通过",
282
- mitigation: "1) 使用返回当前节点进入时间的 query(如 progress.current_time, id=1272)" +
283
- "而非操作时间戳(如 progress.session.forward.time, id=1271);" +
284
- "2) 在时间锁前添加 query > 0 的前置检查;" +
285
- "3) 确认 query 在 Guard 执行时必然有值(查询当前节点属性而非历史操作)",
286
- };
287
- }
288
- return null;
568
+ const str = rootStr(ctx);
569
+ if (!(str.includes("logic_as_u256_greater") && str.includes('"query"')))
570
+ return null;
571
+ return {
572
+ id: "R-X1-02",
573
+ data_source_class: "cross_type",
574
+ level: "medium",
575
+ category: "logic_gap",
576
+ title: "Numeric comparison does not handle the case of query returning null",
577
+ description: "Guard uses logic_as_u256_greater for numeric comparison, and operands come from a query (Type1/2/3 data sources)." +
578
+ "If the query returns null (e.g. object does not exist, field not set), the comparison result may be undefined." +
579
+ "In the WoWok protocol null is usually treated as 0, which may cause unexpected pass.",
580
+ affected_stakeholders: ["customer", "provider"],
581
+ scenario: "Null misjudgment: query returns 0, causing > 0 condition to unexpectedly pass or fail",
582
+ mitigation: "1) Add a query result existence check before comparison;" +
583
+ "2) Use query_reward_record_exists or query_progress_history_find to verify first;" +
584
+ "3) Consider adding a logic_and(object exists, numeric comparison) combination",
585
+ };
289
586
  },
290
587
  },
291
588
  {
292
- id: "R-BR-01",
293
- trigger: "Guard 绑定到已发布的 Machine/Service(不可变)",
589
+ id: "R-X1-03",
590
+ data_source_class: "cross_type",
591
+ trigger: "Guard uses logic_string_contains and may be bypassed by substring matching",
294
592
  check: (ctx) => {
295
- if (ctx.scene && !ctx.scene.mutable_after_publish) {
296
- return {
297
- id: "R-BR-01",
298
- level: "info",
299
- category: "binding_risk",
300
- title: `Guard 绑定到不可变的 ${ctx.scene.host_object}(发布后不可修改)`,
301
- description: `${ctx.scene.host_object} 发布后 ${ctx.scene.binding_field} 不可修改。` +
302
- "如果 Guard 设计有误,需要新建 Machine/Service 并迁移所有引用。" +
303
- "这是设计约束而非风险,但需要确保 Guard 设计正确后再发布。",
304
- affected_stakeholders: ["provider"],
305
- scenario: "不可逆绑定:发布后发现 Guard 设计有误无法修改",
306
- mitigation: "1) 发布前使用 gen_passport 进行充分测试;" +
307
- "2) 测试 expected-pass expected-fail 两种场景;" +
308
- "3) 发布前导出 Guard 备份(guard2file)",
309
- };
310
- }
311
- return null;
593
+ const str = rootStr(ctx);
594
+ if (!(str.includes("logic_string_contains") && !str.includes("logic_string_nocase")))
595
+ return null;
596
+ return {
597
+ id: "R-X1-03",
598
+ data_source_class: "cross_type",
599
+ level: "low",
600
+ category: "logic_gap",
601
+ title: "logic_string_contains substring match may be bypassed",
602
+ description: "Guard uses logic_string_contains for string matching." +
603
+ "Substring matching may be bypassed: e.g. when checking 'Completed', 'NotCompleted' also contains 'Completed'." +
604
+ "In addition, case sensitivity may cause unexpected failures. String data may come from Type1 (query object fields), " +
605
+ "Type3 (submitted values), or the context of a rely-dependent Guard.",
606
+ affected_stakeholders: ["customer", "provider"],
607
+ scenario: "Substring mismatch: 'NotCompleted' contains 'Completed', causing false pass",
608
+ mitigation: "1) Prefer logic_equal for exact matching;" +
609
+ "2) If substring matching is needed, add boundary conditions (e.g. prefix + suffix);" +
610
+ "3) Confirm whether case-insensitivity is needed (use the nocase variant)",
611
+ };
312
612
  },
313
613
  },
314
614
  {
315
- id: "R-BR-02",
316
- trigger: "Repository write_guard 未验证 id_from_submission 类型",
615
+ id: "R-X1-04",
616
+ data_source_class: "cross_type",
617
+ trigger: "Guard root is an identifier directly returning Bool",
317
618
  check: (ctx) => {
318
- if (ctx.scene?.binding_field === "write_guard") {
319
- return {
320
- id: "R-BR-02",
321
- level: "high",
322
- category: "binding_risk",
323
- title: "Repository write_guard 需验证 id_from_submission 类型",
324
- description: "Repository write_guard 的 id_from_submission 引用的 table 条目必须 Address 类型。" +
325
- "data_from_submission 引用的条目必须匹配 Repository value_type。" +
326
- "类型不匹配会导致写入失败或数据损坏。",
327
- affected_stakeholders: ["provider"],
328
- scenario: "类型不匹配:写入失败或数据损坏",
329
- mitigation: "1) 确认 id_from_submission 引用的条目 value_type=Address;" +
330
- "2) 确认 data_from_submission 引用的条目 value_type 匹配 Repository;" +
331
- "3) gen_passport 测试中覆盖写入场景",
332
- };
333
- }
334
- return null;
619
+ if (ctx.root?.type !== "identifier")
620
+ return null;
621
+ const entry = ctx.table.find((e) => e.identifier === ctx.root.identifier);
622
+ const isSubmission = entry?.b_submission ?? false;
623
+ return {
624
+ id: "R-X1-04",
625
+ data_source_class: "cross_type",
626
+ level: isSubmission ? "medium" : "low",
627
+ category: "logic_gap",
628
+ title: "Root directly returns identifier value, no operation logic",
629
+ description: "Guard root is an identifier node, directly returning the value (Bool) from table." +
630
+ (isSubmission
631
+ ? "This entry has b_submission=true (Type3 submitted value); the caller can directly submit true to bypass all verification."
632
+ : "This entry has b_submission=false (Type1 constant); the value is fixed but the Guard has no actual verification logic."),
633
+ affected_stakeholders: ["customer", "provider"],
634
+ scenario: isSubmission
635
+ ? "Bypass verification: caller submits true to pass the Guard directly"
636
+ : "No verification logic: Guard always returns a fixed value, providing no actual protection",
637
+ mitigation: isSubmission
638
+ ? "1) Ensure the entry referenced by identifier has b_submission=false (constant);" +
639
+ "2) Or add an additional logic_and condition for cross-validation"
640
+ : "1) Add actual verification logic (query + logic comparison);" +
641
+ "2) Evaluate whether this Guard needs to exist",
642
+ };
335
643
  },
336
644
  },
337
645
  {
338
- id: "R-DR-01",
339
- trigger: "Guard 使用 rely 但依赖的 Guard 可能失效",
646
+ id: "R-X1-05",
647
+ data_source_class: "cross_type",
648
+ trigger: "Allocation Guard may cause some participants to be skipped",
340
649
  check: (ctx) => {
341
- if (ctx.rely && ctx.rely.guards.length > 0) {
342
- const logic = ctx.rely.logic_or ? "OR" : "AND";
343
- return {
344
- id: "R-DR-01",
345
- level: ctx.rely.logic_or ? "low" : "medium",
346
- category: "dependency_risk",
347
- title: `rely ${logic} 依赖 ${ctx.rely.guards.length} 个外部 Guard`,
348
- description: `Guard 使用 rely ${logic} 依赖 ${ctx.rely.guards.length} 个外部 Guard。` +
349
- (ctx.rely.logic_or
350
- ? "OR 逻辑:任一依赖通过即可。如果某个依赖 Guard 设计宽松,可能成为绕过入口。"
351
- : "AND 逻辑:所有依赖必须通过。如果某个依赖 Guard 失效或被删除,当前 Guard 将永远失败。"),
352
- affected_stakeholders: ["customer", "provider"],
353
- scenario: ctx.rely.logic_or
354
- ? "依赖绕过:宽松的依赖 Guard 成为攻击入口"
355
- : "依赖失效:依赖 Guard 被删除导致当前 Guard 永远失败",
356
- mitigation: ctx.rely.logic_or
357
- ? "1) 审查每个依赖 Guard 的严格程度;2) 确保所有依赖 Guard 至少与当前 Guard 同等严格"
358
- : "1) 确认所有依赖 Guard 的 rep=true 且不可删除;2) 考虑是否真的需要 AND 依赖",
359
- };
360
- }
361
- return null;
650
+ if (ctx.scene?.binding_field !== "order_allocators")
651
+ return null;
652
+ return {
653
+ id: "R-X1-05",
654
+ data_source_class: "cross_type",
655
+ level: "medium",
656
+ category: "game_theory",
657
+ title: "order_allocators first-match-wins may cause unfair allocation",
658
+ description: "order_allocators uses a first-match-wins strategy: the first Allocator whose Guard passes executes the allocation." +
659
+ "If the order is poorly designed, some participants may never get allocated." +
660
+ "For example: if the first Allocator's Guard condition is too broad, subsequent more precise Allocators will never execute." +
661
+ "Allocation Guards typically combine Type1 (query order fields), Type3 (submit Order address), and Type4 (Signer) data.",
662
+ affected_stakeholders: ["provider"],
663
+ scenario: "Allocation skip: the Provider's allocation Allocator is skipped by preceding broad conditions",
664
+ mitigation: "1) Ensure the order_allocators order goes from strict to loose;" +
665
+ "2) Each Allocator's Guard conditions should be mutually exclusive;" +
666
+ "3) Consider using logic_or dependencies rather than sequential ordering",
667
+ };
668
+ },
669
+ },
670
+ {
671
+ id: "R-X1-06",
672
+ data_source_class: "cross_type",
673
+ trigger: "Workflow Guard time-lock may lock the disadvantaged party",
674
+ check: (ctx) => {
675
+ const str = rootStr(ctx);
676
+ if (!(str.includes("clock") && str.includes("calc_number_add")))
677
+ return null;
678
+ return {
679
+ id: "R-X1-06",
680
+ data_source_class: "cross_type",
681
+ level: "medium",
682
+ category: "game_theory",
683
+ title: "Time-lock may lock the disadvantaged party",
684
+ description: "Guard uses a time-lock (context(Clock) + calc_number_add). The time-lock itself is neutral, " +
685
+ "but the locking direction needs to be checked: if the Customer is locked to pay first but cannot accept in time, the Provider gains advantage; " +
686
+ "if the Provider is locked to deliver first but cannot receive payment in time, the Customer gains advantage." +
687
+ "The time-lock combines Type4 (Clock) and Type1/2 (query time fields) data sources.",
688
+ affected_stakeholders: ["customer", "provider"],
689
+ scenario: "Time asymmetry: one party is locked by time while the other has freedom of action",
690
+ mitigation: "1) Ensure the time-lock is symmetric (both parties have time constraints);" +
691
+ "2) Provide an exit mechanism after the time-lock expires (e.g. auto-refund/auto-complete);" +
692
+ "3) Design corresponding timeout Forward nodes in the Machine",
693
+ };
694
+ },
695
+ },
696
+ {
697
+ id: "R-X1-07",
698
+ data_source_class: "cross_type",
699
+ trigger: "Arbitration usage_guard may block legitimate disputes",
700
+ check: (ctx) => {
701
+ if (ctx.scene?.binding_field !== "usage_guard")
702
+ return null;
703
+ const str = rootStr(ctx);
704
+ const andCount = (str.match(/"logic_and"/g) || []).length;
705
+ if (andCount < 3)
706
+ return null;
707
+ return {
708
+ id: "R-X1-07",
709
+ data_source_class: "cross_type",
710
+ level: "high",
711
+ category: "game_theory",
712
+ title: "usage_guard has too many conditions and may block legitimate disputes",
713
+ description: `Detected ${andCount} logic_and combinations. Too many AND conditions make the dispute threshold too high, ` +
714
+ "the Customer may not be able to initiate a legitimate dispute, and the Provider gains an unfair advantage." +
715
+ "usage_guard typically combines Type4 (Signer identity), Type3 (submit Passport credentials), " +
716
+ "and Type1 (query EntityRegistrar verification) data sources.",
717
+ affected_stakeholders: ["customer"],
718
+ scenario: "Dispute threshold too high: Customer cannot initiate a legitimate dispute",
719
+ mitigation: "1) Re-evaluate the necessity of each AND condition;" +
720
+ "2) Consider using logic_or to lower the threshold;" +
721
+ "3) Move non-essential conditions to voting_guard rather than usage_guard",
722
+ };
723
+ },
724
+ },
725
+ {
726
+ id: "R-X1-08",
727
+ data_source_class: "cross_type",
728
+ trigger: "Guard is bound to a published Machine/Service (immutable)",
729
+ check: (ctx) => {
730
+ if (!ctx.scene || ctx.scene.mutable_after_publish)
731
+ return null;
732
+ return {
733
+ id: "R-X1-08",
734
+ data_source_class: "cross_type",
735
+ level: "info",
736
+ category: "binding_risk",
737
+ title: `Guard is bound to immutable ${ctx.scene.host_object} (cannot be modified after publish)`,
738
+ description: `After ${ctx.scene.host_object} is published, ${ctx.scene.binding_field} cannot be modified.` +
739
+ "If the Guard design is flawed, a new Machine/Service must be created and all references migrated." +
740
+ "This is a design constraint rather than a risk, but the Guard design must be correct before publishing.",
741
+ affected_stakeholders: ["provider"],
742
+ scenario: "Irreversible binding: after publish, a flawed Guard design cannot be modified",
743
+ mitigation: "1) Use gen_passport for thorough testing before publishing;" +
744
+ "2) Test both expected-pass and expected-fail scenarios;" +
745
+ "3) Export a Guard backup (guard2file) before publishing",
746
+ };
747
+ },
748
+ },
749
+ {
750
+ id: "R-X1-09",
751
+ data_source_class: "cross_type",
752
+ trigger: "Guard uses rely but the dependent Guard may fail",
753
+ check: (ctx) => {
754
+ if (!ctx.rely || ctx.rely.guards.length === 0)
755
+ return null;
756
+ const logic = ctx.rely.logic_or ? "OR" : "AND";
757
+ return {
758
+ id: "R-X1-09",
759
+ data_source_class: "cross_type",
760
+ level: ctx.rely.logic_or ? "low" : "medium",
761
+ category: "dependency_risk",
762
+ title: `rely ${logic} depends on ${ctx.rely.guards.length} external Guards`,
763
+ description: `Guard uses rely ${logic} to depend on ${ctx.rely.guards.length} external Guards.` +
764
+ (ctx.rely.logic_or
765
+ ? "OR logic: any one passing is enough. If a dependent Guard is loosely designed, it may become a bypass entry point."
766
+ : "AND logic: all dependencies must pass. If a dependent Guard fails or is deleted, the current Guard will always fail.") +
767
+ " rely-dependent Guards must have rep=true (repository.data queries do not depend on runtime submission).",
768
+ affected_stakeholders: ["customer", "provider"],
769
+ scenario: ctx.rely.logic_or
770
+ ? "Dependency bypass: a loose dependent Guard becomes an attack entry point"
771
+ : "Dependency failure: a dependent Guard is deleted, causing the current Guard to always fail",
772
+ mitigation: ctx.rely.logic_or
773
+ ? "1) Review the strictness of each dependent Guard; 2) Ensure all dependent Guards are at least as strict as the current Guard"
774
+ : "1) Confirm all dependent Guards have rep=true and cannot be deleted; 2) Consider whether AND dependency is truly needed",
775
+ };
776
+ },
777
+ },
778
+ {
779
+ id: "R-X1-10",
780
+ data_source_class: "cross_type",
781
+ trigger: "Guard uses repository.data query (query 1167) and the Repository has quote_guard set",
782
+ check: (ctx) => {
783
+ const str = rootStr(ctx);
784
+ if (!(str.includes("repository") && str.includes('"query"')))
785
+ return null;
786
+ return {
787
+ id: "R-X1-10",
788
+ data_source_class: "cross_type",
789
+ level: "medium",
790
+ category: "impack_risk",
791
+ title: "repository.data query's quote_guard is invalid in the verify phase (impack_list is empty)",
792
+ description: "Guard queries repository.data (query 1167). According to passport.move#L289 and passport.rs implementation, " +
793
+ "the impack_list is always empty in the verify phase (only filled after result_for_permission).\n" +
794
+ "Therefore in check_repository_quote_guard (inside query 1167), " +
795
+ "if the Repository policy's quote_guard is Some(addr), " +
796
+ "since addr is not in the empty list, it will always report IMPACK_GUARD_NOT_FOUND.\n" +
797
+ "Only when quote_guard==None can query 1167 pass.\n" +
798
+ "This means: querying Repository data with a quote_guard in the Guard will always fail.",
799
+ affected_stakeholders: ["customer", "provider"],
800
+ scenario: "quote_guard invalidation: impack_list is empty in the verify phase, causing the 1167 query to always fail",
801
+ mitigation: "1) Confirm the queried Repository's quote_guard is None;" +
802
+ "2) If quote authentication is needed, modify the verify loop in the Move layer to maintain impack_list;" +
803
+ "3) Document this limitation clearly: Repositories with quote_guard cannot be queried in the Guard;" +
804
+ "4) Consider using other data sources instead of Repository.data query",
805
+ };
806
+ },
807
+ },
808
+ {
809
+ id: "R-X1-11",
810
+ data_source_class: "cross_type",
811
+ trigger: "Guard uses rely to depend on other Guards; need to confirm the rep flag of dependent Guards is correct",
812
+ check: (ctx) => {
813
+ if (!ctx.rely || ctx.rely.guards.length === 0)
814
+ return null;
815
+ const str = rootStr(ctx);
816
+ const queriesRepoData = str.includes("repository") && str.includes('"query"');
817
+ const hasRepoSubmission = ctx.table.some((e) => e.b_submission && e.value_type === "Address" && e.object_type === "Repository");
818
+ return {
819
+ id: "R-X1-11",
820
+ data_source_class: "cross_type",
821
+ level: queriesRepoData && hasRepoSubmission ? "medium" : "low",
822
+ category: "rep_risk",
823
+ title: "rep semantics needs confirmation (when repository.data query depends on submission, rep=false and cannot be depended on)",
824
+ description: "Guard uses rely to depend on external Guards. In the WoWok protocol, rely-dependent Guards must have rep=true.\n" +
825
+ "Precise rep semantics (guard.rs#L483-L492): repository.data query (query 1167) does not depend on runtime submission.\n" +
826
+ " - rep=true: repository.data query is fully constant-driven (object address b_submission=false and query parameters are not submission)\n" +
827
+ " - rep=false: repository address comes from runtime submission, or query parameters come from submission\n" +
828
+ "Guards with rep=false cannot appear in other Guards' rely lists.\n" +
829
+ (queriesRepoData && hasRepoSubmission
830
+ ? "\nWarning: the current Guard itself queries repository.data and the Repository address comes from submission, " +
831
+ "so its own rep=false and cannot be depended on by other Guards."
832
+ : ""),
833
+ affected_stakeholders: ["provider"],
834
+ scenario: "rep misjudgment: depending on a rep=false Guard causes creation failure, or its own rep=false is wrongly depended on",
835
+ mitigation: "1) Confirm all rely-dependent Guards have rep=true (do not depend on submission to query repository.data);" +
836
+ "2) If the current Guard queries repository.data and the address comes from submission, its own rep=false; annotate that it cannot be depended on;" +
837
+ "3) rep is not 'no Repository dependency', but 'repository.data query does not depend on submission';" +
838
+ "4) Use wowok_buildin_info to query the full definition of the rep flag",
839
+ evidence: queriesRepoData && hasRepoSubmission
840
+ ? "Self rep=false (repository.data query depends on submission)"
841
+ : "Self rep status to be confirmed",
842
+ };
843
+ },
844
+ },
845
+ {
846
+ id: "R-X1-12",
847
+ data_source_class: "cross_type",
848
+ trigger: "Guard table contains b_submission=true entries with vague name descriptions or missing binding_constraint",
849
+ check: (ctx) => {
850
+ const submissionEntries = ctx.table.filter((e) => e.b_submission);
851
+ if (submissionEntries.length === 0)
852
+ return null;
853
+ const vagueEntries = submissionEntries.filter((e) => {
854
+ const name = e.name ?? "";
855
+ return name.length < 10 || /^[a-z_]+$/i.test(name) || name === "";
856
+ });
857
+ if (vagueEntries.length === 0)
858
+ return null;
859
+ return {
860
+ id: "R-X1-12",
861
+ data_source_class: "cross_type",
862
+ level: "medium",
863
+ category: "submission_forgery",
864
+ title: "Submitted data entry description is vague (retained_submission risk: caller may submit wrong data)",
865
+ description: `Guard table has ${vagueEntries.length} b_submission=true entries with vague name descriptions.\n` +
866
+ "The name of a submission entry is the only data contract between the Guard and the caller.\n" +
867
+ "If the name description is unclear (e.g. only 'addr' / 'order_id'), the caller may:\n" +
868
+ " - Submit a wrong-typed object address (e.g. submit a Service address instead of an Order address)\n" +
869
+ " - Submit a wrong value (e.g. inconsistent amount units)\n" +
870
+ " - Confuse submission slots of different Guards in multi-Guard scenarios\n" +
871
+ "Vague submission entries are 'retained' in the table but provide no effective data constraints, " +
872
+ "equivalent to the Guard accepting arbitrary data without validating its semantic correctness.",
873
+ affected_stakeholders: ["customer", "provider"],
874
+ scenario: "Caller submits wrong data due to vague name description; the Guard passes verification but the business logic is wrong",
875
+ mitigation: "1) Each submission entry's name should be a complete natural-language description (e.g. 'The order ID that identifies the target Order for verification');\n" +
876
+ "2) Avoid Technical identifiers as name (e.g. 'order_id' should become 'Order ID, used to query progress status');\n" +
877
+ "3) Add failure_conditions in binding_hint to clarify failure scenarios;\n" +
878
+ "4) Consider adding type validation logic in root (e.g. query the submitted object's objectType and match with expectation)",
879
+ evidence: `Vague entries: ${vagueEntries.map((e) => `identifier=${e.identifier}, name="${e.name}"`).join("; ")}`,
880
+ };
881
+ },
882
+ },
883
+ {
884
+ id: "R-X1-13",
885
+ data_source_class: "cross_type",
886
+ trigger: "Guard references its host object (Host Object) itself — circular reference pattern",
887
+ check: (ctx) => {
888
+ if (!ctx.scene)
889
+ return null;
890
+ const hostType = ctx.scene.host_object;
891
+ const circularEntry = ctx.table.find((e) => !e.b_submission && e.value_type === "Address" &&
892
+ e.object_type === hostType);
893
+ if (!circularEntry)
894
+ return null;
895
+ return {
896
+ id: "R-X1-13",
897
+ data_source_class: "cross_type",
898
+ level: "medium",
899
+ category: "binding_risk",
900
+ title: "Circular reference pattern (Guard references the host object itself; note creation order)",
901
+ description: `In the Guard's table, identifier=${circularEntry.identifier} has object_type equal to the host object type "${hostType}".\n` +
902
+ "Circular reference pattern: the Guard is bound to a Host Object (e.g. Service.buy_guard) and also queries the Host Object's own fields.\n" +
903
+ "Creation order constraints:\n" +
904
+ " 1. The Host Object must be created first (unpublished state)\n" +
905
+ " 2. When the Guard is created, the table uses the Host Object's name (not address); the SDK resolves it at binding time\n" +
906
+ " 3. After the Guard is bound to the Host Object, the Host Object is published\n" +
907
+ "Risks:\n" +
908
+ " - If the Host Object is already published (immutable), the Guard can no longer be bound\n" +
909
+ " - If the Host Object's name is modified after the Guard is created, the Guard's table reference is invalidated\n" +
910
+ " - Circular reference makes the Guard not independently testable (the Host Object must exist to verify)",
911
+ affected_stakeholders: ["provider"],
912
+ scenario: "Guard queries fields of the Service/Machine/Reward it is bound to; wrong creation order causes binding failure",
913
+ mitigation: "1) Ensure the Host Object is unpublished (bPublished=false) when creating and binding the Guard;\n" +
914
+ "2) Use the Host Object's name rather than address in table (the SDK auto-resolves);\n" +
915
+ "3) Perform Guard verification tests after the Host Object is published;\n" +
916
+ "4) Use guard2file to export a Guard backup for restoring references during rebuild",
917
+ evidence: `Circular reference: identifier=${circularEntry.identifier}, object_type="${hostType}" → ${ctx.scene.host_object}`,
918
+ };
362
919
  },
363
920
  },
364
921
  ];
@@ -381,6 +938,11 @@ export function assessGuardRisks(params) {
381
938
  }
382
939
  }
383
940
  const overall_risk = getOverallRiskLevel(risks);
941
+ const risks_by_data_source = {};
942
+ for (const risk of risks) {
943
+ risks_by_data_source[risk.data_source_class] =
944
+ (risks_by_data_source[risk.data_source_class] ?? 0) + 1;
945
+ }
384
946
  const stakeholder_advice = generateStakeholderAdvice(risks, params.scene);
385
947
  const needs_human_review = risks.some((r) => r.level === "critical") ||
386
948
  risks.filter((r) => r.level === "high").length >= 2;
@@ -390,6 +952,7 @@ export function assessGuardRisks(params) {
390
952
  scene: params.scene,
391
953
  risks,
392
954
  overall_risk,
955
+ risks_by_data_source,
393
956
  stakeholder_advice,
394
957
  needs_human_review,
395
958
  };
@@ -419,22 +982,22 @@ function generateStakeholderAdvice(risks, scene) {
419
982
  case "buy_guard":
420
983
  if (!advice.customer)
421
984
  advice.customer = [];
422
- advice.customer.push("购买前确认 buy_guard 的验证逻辑,确保你的身份/凭证能通过验证");
985
+ advice.customer.push("Before purchase, verify the buy_guard logic to ensure your identity/credentials can pass validation");
423
986
  break;
424
987
  case "order_allocators":
425
988
  if (!advice.provider)
426
989
  advice.provider = [];
427
- advice.provider.push("确认 order_allocators 顺序,确保你的分账 Allocator 不会被前面的条件跳过");
990
+ advice.provider.push("Verify the order_allocators ordering to ensure your Allocator will not be skipped by preceding conditions");
428
991
  break;
429
992
  case "voting_guard":
430
993
  if (!advice.arbitrator)
431
994
  advice.arbitrator = [];
432
- advice.arbitrator.push("确认投票权重来源可信,建议从 EntityRegistrar 查询而非用户提交");
995
+ advice.arbitrator.push("Confirm the voting weight source is trustworthy; prefer querying EntityRegistrar rather than user-submitted data");
433
996
  break;
434
997
  case "Forward.guard":
435
998
  if (!advice.customer)
436
999
  advice.customer = [];
437
- advice.customer.push("确认 Forward.guard 的条件,确保你能满足推进条件,避免资金被锁定");
1000
+ advice.customer.push("Verify the Forward.guard conditions to ensure you can satisfy the advancement criteria and avoid locked funds");
438
1001
  break;
439
1002
  }
440
1003
  }
@@ -442,12 +1005,12 @@ function generateStakeholderAdvice(risks, scene) {
442
1005
  }
443
1006
  function generateGuardSummary(params) {
444
1007
  const parts = [];
445
- parts.push(`table 条目数: ${params.table.length}`);
1008
+ parts.push(`table entries: ${params.table.length}`);
446
1009
  const submissions = params.table.filter((e) => e.b_submission).length;
447
- parts.push(`提交数据: ${submissions} 条`);
448
- parts.push(`root 类型: ${params.root?.type ?? "unknown"}`);
1010
+ parts.push(`submitted data: ${submissions} entries`);
1011
+ parts.push(`root type: ${params.root?.type ?? "unknown"}`);
449
1012
  if (params.rely) {
450
- parts.push(`rely: ${params.rely.guards.length} 个依赖 (${params.rely.logic_or ? "OR" : "AND"})`);
1013
+ parts.push(`rely: ${params.rely.guards.length} dependencies (${params.rely.logic_or ? "OR" : "AND"})`);
451
1014
  }
452
1015
  return parts.join(", ");
453
1016
  }
@@ -472,3 +1035,12 @@ export function getRiskSummary(assessment) {
472
1035
  .map((r) => r.title),
473
1036
  };
474
1037
  }
1038
+ export function getRiskRulesByDataSourceClass(dataSourceClass) {
1039
+ return RISK_RULES.filter((r) => r.data_source_class === dataSourceClass);
1040
+ }
1041
+ export function getAllRiskRules() {
1042
+ return RISK_RULES;
1043
+ }
1044
+ export function getRiskRulesCount() {
1045
+ return RISK_RULES.length;
1046
+ }