oc-one-guard 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/state.js CHANGED
@@ -18,6 +18,7 @@
18
18
 
19
19
  import { getWrite, referencedWrites } from "./written.js"
20
20
  import { requireScope } from "./scope.js"
21
+ import { canonicalTool, pathsOf } from "./tools.js"
21
22
 
22
23
  const URL_PATTERN = /\b(?:https?|ftps?|sftp|scp|rsync|ssh|git):\/\/[^\s"'`<>|;&]+/gi
23
24
 
@@ -51,7 +52,7 @@ const LOCAL_ONLY_TOOLS = new Set([
51
52
  * file and execute it in a later call. Their content is judged when it
52
53
  * contains something that moves data.
53
54
  */
54
- const CONTENT_TOOLS = new Set(["write", "edit", "apply_patch"])
55
+ const CONTENT_TOOLS = new Set(["write", "edit", "patch"])
55
56
 
56
57
  /**
57
58
  * Primitives that reach the network, matched against shell text.
@@ -138,13 +139,14 @@ export function runsLocalScript(text) {
138
139
  * @returns {string[]}
139
140
  */
140
141
  export function extractHosts(tool, args) {
142
+ const name = canonicalTool(tool)
141
143
  const chunks = []
142
- if (tool === "bash" || tool === "shell" || tool === "run") {
144
+ if (name === "shell") {
143
145
  if (typeof args?.command === "string") chunks.push(args.command)
144
- } else if (tool === "webfetch") {
146
+ } else if (name === "webfetch") {
145
147
  if (typeof args?.url === "string") chunks.push(args.url)
146
- } else if (CONTENT_TOOLS.has(tool)) {
147
- chunks.push(writtenContent(tool, args))
148
+ } else if (CONTENT_TOOLS.has(name)) {
149
+ chunks.push(writtenContent(name, args))
148
150
  }
149
151
 
150
152
  const hosts = new Set()
@@ -170,20 +172,14 @@ export function extractPaths(tool, args) {
170
172
  const push = (value) => {
171
173
  if (typeof value === "string" && value.trim()) paths.push(value.trim())
172
174
  }
173
- switch (tool) {
175
+ switch (canonicalTool(tool)) {
174
176
  case "read":
175
- push(args?.filePath)
176
- break
177
177
  case "write":
178
- push(args?.filePath)
179
- break
180
178
  case "edit":
181
- push(args?.filePath)
182
- break
183
- case "apply_patch":
184
- push(args?.filePath)
179
+ case "patch":
180
+ pathsOf(args).forEach(push)
185
181
  break
186
- case "bash":
182
+ case "shell":
187
183
  push(args?.workdir)
188
184
  break
189
185
  default:
@@ -287,15 +283,16 @@ export function fullText(tool, args) {
287
283
  * helper is used so the two agree.
288
284
  */
289
285
  export function writtenContent(tool, args) {
286
+ const name = canonicalTool(tool)
290
287
  if (!args || typeof args !== "object") return ""
291
- if (tool === "write") return typeof args.content === "string" ? args.content : ""
292
- if (tool === "edit") {
288
+ if (name === "write") return typeof args.content === "string" ? args.content : ""
289
+ if (name === "edit") {
293
290
  const parts = []
294
291
  if (typeof args.newString === "string") parts.push(args.newString)
295
292
  if (typeof args.oldString === "string") parts.push(args.oldString)
296
293
  return parts.join("\n")
297
294
  }
298
- if (tool === "apply_patch") {
295
+ if (name === "patch") {
299
296
  const parts = []
300
297
  if (typeof args.patchText === "string") parts.push(args.patchText)
301
298
  if (typeof args.patch === "string") parts.push(args.patch)
@@ -316,17 +313,18 @@ export function writtenContent(tool, args) {
316
313
  */
317
314
  export function hasOutboundSurface(tool, args, directory, scope) {
318
315
  requireScope(scope)
319
- if (LOCAL_ONLY_TOOLS.has(tool)) {
316
+ const name = canonicalTool(tool)
317
+ if (LOCAL_ONLY_TOOLS.has(name)) {
320
318
  // A file write can stage a payload that a later call executes, so the
321
319
  // content decides.
322
- if (CONTENT_TOOLS.has(tool)) {
323
- const content = writtenContent(tool, args)
320
+ if (CONTENT_TOOLS.has(name)) {
321
+ const content = writtenContent(name, args)
324
322
  return hasTransferPrimitive(content)
325
323
  }
326
324
  return false
327
325
  }
328
326
 
329
- if (tool === "bash" || tool === "shell" || tool === "run") {
327
+ if (name === "shell") {
330
328
  const command = typeof args?.command === "string" ? args.command : ""
331
329
  if (hasTransferPrimitive(command)) return true
332
330
  if (runsLocalScript(command)) return true
@@ -336,7 +334,7 @@ export function hasOutboundSurface(tool, args, directory, scope) {
336
334
  return false
337
335
  }
338
336
 
339
- if (tool === "webfetch") {
337
+ if (name === "webfetch") {
340
338
  if (args && Object.keys(args).some((key) => !["url", "format", "timeout"].includes(key))) return true
341
339
  // A GET can still carry data in the path or the query string.
342
340
  const url = typeof args?.url === "string" ? args.url : ""
package/src/tools.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Tool identifiers shared by OpenCode 1.x and 2.x.
3
+ *
4
+ * Canonical names are the OpenCode 2.x ids. Names from 1.x are mapped onto
5
+ * them, so policy lists and checks work for both hosts.
6
+ */
7
+
8
+ const ALIASES = new Map([
9
+ ["bash", "shell"],
10
+ ["apply_patch", "patch"],
11
+ ["task", "subagent"],
12
+ ])
13
+
14
+ export function canonicalTool(name) {
15
+ if (typeof name !== "string") return name
16
+ return ALIASES.get(name) ?? name
17
+ }
18
+
19
+ export function isMutationTool(tool) {
20
+ return ["write", "edit", "patch"].includes(canonicalTool(tool))
21
+ }
22
+
23
+ export function listsTool(list, tool) {
24
+ const wanted = canonicalTool(tool)
25
+ return list.some(entry => typeof entry === "string" && canonicalTool(entry) === wanted)
26
+ }
27
+
28
+ // 1.x write, edit and read take `filePath`; 2.x takes `path`. Every listed
29
+ // path counts as evidence, so a call that carries both is judged on both.
30
+ export function pathsOf(args) {
31
+ const paths = []
32
+ for (const key of ["filePath", "path"]) {
33
+ const value = args?.[key]
34
+ if (typeof value === "string" && value && !paths.includes(value)) paths.push(value)
35
+ }
36
+ return paths
37
+ }