@oracle-agent/oracle 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oracle-agent/oracle",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "Oracle: standalone prepare-only multichain agent control plane with optional Hermes integration. Policy-bounded intents for a user-signed wallet; the public package never takes your key.",
5
5
  "license": "BUSL-1.1",
6
6
  "type": "module",
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  // Pre-publication secret scan. Fails CLOSED: any hit is an error.
3
3
  //
4
- // Checks the working tree AND every blob in git history, because a secret
5
- // removed in a later commit is still public the moment the repo is.
4
+ // Checks the working tree and every blob reachable from HEAD. Set
5
+ // ORACLE_SCAN_ALL_REFS=1 for an explicit audit of every fetched ref.
6
6
 
7
7
  import { execFileSync } from "node:child_process";
8
8
  import { validateMnemonic } from "@scure/bip39";
@@ -64,6 +64,7 @@ const ALLOWED_VALUES = [
64
64
 
65
65
  const PUBLIC_EMAIL_RE = /^(?:[0-9]+\+)?[a-z0-9_.-]+\[?bot\]?@users\.noreply\.github\.com$|^oracle@users\.noreply\.github\.com$|^noreply@github\.com$/i;
66
66
  const PRIVATE_EMAIL_RE = /@(gmail|icloud|me|yahoo|outlook|hotmail|protonmail)\.com$|@(local|gmk)$|^fleet@|^agent@/i;
67
+ const HISTORY_SCOPE = process.env.ORACLE_SCAN_ALL_REFS === "1" ? "--all" : "HEAD";
67
68
 
68
69
  function sh(cmd, args) {
69
70
  return execFileSync(cmd, args, { cwd: ROOT, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
@@ -99,7 +100,7 @@ function parseEmail(identity) {
99
100
  }
100
101
 
101
102
  function scanIdentities(findings) {
102
- const identities = new Set(sh("git", ["log", "--all", "--format=%an <%ae>|%cn <%ce>"]).split(/[\n|]/).filter(Boolean));
103
+ const identities = new Set(sh("git", ["log", HISTORY_SCOPE, "--format=%an <%ae>|%cn <%ce>"]).split(/[\n|]/).filter(Boolean));
103
104
  for (const identity of identities) {
104
105
  const email = parseEmail(identity);
105
106
  if (!email) continue;
@@ -128,7 +129,7 @@ for (const rel of tracked) {
128
129
  }
129
130
 
130
131
  // --- 2. every blob in history ---
131
- const revs = sh("git", ["rev-list", "--all"]).split("\n").filter(Boolean);
132
+ const revs = sh("git", ["rev-list", HISTORY_SCOPE]).split("\n").filter(Boolean);
132
133
  const seen = new Set();
133
134
  let historyBlobs = 0;
134
135
  for (const rev of revs) {
@@ -20,6 +20,8 @@ import { spawnSync } from "node:child_process";
20
20
  import { oracleConfigDir } from "../paths.mjs";
21
21
  import { loadPlugin, scanInstalledPlugins, SCHEMA_ID } from "../../plugins/agent-plugin-loader.mjs";
22
22
 
23
+ const INSTALLED_PLUGIN_NAME_RE = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
24
+
23
25
  function pluginsDir() {
24
26
  const dir = path.join(oracleConfigDir(), "plugins");
25
27
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
@@ -124,12 +126,44 @@ function cmdRemove(name) {
124
126
  return 1;
125
127
  }
126
128
 
127
- const dest = path.join(pluginsDir(), name);
129
+ if (!INSTALLED_PLUGIN_NAME_RE.test(name) || path.basename(name) !== name || path.isAbsolute(name)) {
130
+ process.stderr.write(`invalid plugin name "${name}"\n`);
131
+ return 1;
132
+ }
133
+
134
+ const root = pluginsDir();
135
+ const rootStat = fs.lstatSync(root);
136
+ if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
137
+ process.stderr.write("refusing to remove plugin: plugin root is not a regular directory\n");
138
+ return 1;
139
+ }
140
+
141
+ const dest = path.resolve(root, name);
142
+ const relative = path.relative(root, dest);
143
+ if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
144
+ process.stderr.write(`invalid plugin name "${name}"\n`);
145
+ return 1;
146
+ }
147
+
128
148
  if (!fs.existsSync(dest)) {
129
149
  process.stderr.write(`plugin "${name}" not found\n`);
130
150
  return 1;
131
151
  }
132
152
 
153
+ const stat = fs.lstatSync(dest);
154
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
155
+ process.stderr.write(`refusing to remove plugin "${name}": path is not a regular plugin directory\n`);
156
+ return 1;
157
+ }
158
+
159
+ const rootReal = fs.realpathSync(root);
160
+ const destReal = fs.realpathSync(dest);
161
+ const realRelative = path.relative(rootReal, destReal);
162
+ if (!realRelative || realRelative === ".." || realRelative.startsWith(`..${path.sep}`) || path.isAbsolute(realRelative)) {
163
+ process.stderr.write(`refusing to remove plugin "${name}": path escapes the plugin directory\n`);
164
+ return 1;
165
+ }
166
+
133
167
  fs.rmSync(dest, { recursive: true, force: true });
134
168
  process.stdout.write(`removed plugin "${name}"\n`);
135
169
  return 0;
@@ -38,25 +38,6 @@ export const OPERATOR_NOT_INSTALLED_MESSAGE = `oracle: signing is unavailable in
38
38
  export const SIGN_HINT =
39
39
  "hint: run 'oracle sign init' to provision keys, or 'oracle sign doctor --json' for detail.";
40
40
 
41
- function whichOnPath(binName) {
42
- const pathEnv = process.env.PATH || "";
43
- const parts = pathEnv.split(path.delimiter).filter(Boolean);
44
- const exts = process.platform === "win32" ? ["", ".cmd", ".exe", ".bat"] : [""];
45
- for (const dir of parts) {
46
- for (const ext of exts) {
47
- const candidate = path.join(dir, binName + ext);
48
- try {
49
- if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
50
- return candidate;
51
- }
52
- } catch {
53
- // ignore
54
- }
55
- }
56
- }
57
- return null;
58
- }
59
-
60
41
  function binsFromDir(binDir) {
61
42
  const bins = {};
62
43
  for (const name of OPERATOR_BIN_NAMES) {
@@ -132,47 +113,6 @@ function tryResolvePackage(fromPaths) {
132
113
  return null;
133
114
  }
134
115
 
135
- function tryCapsOnPath() {
136
- const capsBin = whichOnPath("oracle-operator-caps");
137
- if (!capsBin) return null;
138
- const r = spawnSync(capsBin, [], {
139
- encoding: "utf8",
140
- timeout: 5000,
141
- env: process.env,
142
- });
143
- if (r.status !== 0 || !r.stdout) return null;
144
- try {
145
- const report = JSON.parse(r.stdout);
146
- if (!report || typeof report !== "object" || !report.bins) return null;
147
- return {
148
- ok: true,
149
- binDir: path.dirname(Object.values(report.bins)[0] || capsBin),
150
- version: report.version || "unknown",
151
- bins: report.bins,
152
- source: "caps-path",
153
- controlMcp: report.controlMcp,
154
- };
155
- } catch {
156
- return null;
157
- }
158
- }
159
-
160
- function tryPathProbe() {
161
- const bins = {};
162
- for (const name of OPERATOR_BIN_NAMES) {
163
- const hit = whichOnPath(name);
164
- if (hit) bins[name] = hit;
165
- }
166
- if (!Object.keys(bins).length) return null;
167
- return {
168
- ok: true,
169
- binDir: path.dirname(Object.values(bins)[0]),
170
- version: "unknown",
171
- bins,
172
- source: "path-probe",
173
- };
174
- }
175
-
176
116
  export function resolveOperator() {
177
117
  if (process.env.ORACLE_PUBLIC_DESKTOP === "1") return { ok: false, source: "public-desktop" };
178
118
  const envDir = process.env.ORACLE_OPERATOR_BIN_DIR;
@@ -193,10 +133,7 @@ export function resolveOperator() {
193
133
  return { ok: true, binDir: abs, version, bins, source: "env:ORACLE_OPERATOR_BIN_DIR" };
194
134
  }
195
135
 
196
- const fromPkg =
197
- tryResolvePackage([process.cwd(), PACKAGE_ROOT]) ||
198
- tryCapsOnPath() ||
199
- tryPathProbe();
136
+ const fromPkg = tryResolvePackage([process.cwd(), PACKAGE_ROOT]);
200
137
  return fromPkg || { ok: false };
201
138
  }
202
139