ruflo 3.7.0-alpha.9 → 3.8.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/README.md CHANGED
@@ -6,12 +6,20 @@
6
6
  [![Goal Planner — goal.ruv.io](https://img.shields.io/badge/_Goal_Planner-goal.ruv.io-8b5cf6?style=for-the-badge&logoColor=white&logo=react)](https://goal.ruv.io/)
7
7
  [![Live Agents — goal.ruv.io/agents](https://img.shields.io/badge/_Live_Agents-goal.ruv.io%2Fagents-10b981?style=for-the-badge&logoColor=white&logo=react)](https://goal.ruv.io/agents)
8
8
 
9
+ [![npm version (ruflo)](https://img.shields.io/npm/v/ruflo?label=ruflo&style=for-the-badge&logo=npm&color=cb3837)](https://www.npmjs.com/package/ruflo)
10
+ [![Ecosystem downloads](https://img.shields.io/badge/ecosystem%20downloads-22.2M%2B-blue?style=for-the-badge&logo=npm)](https://github.com/ruvnet/ruflo/blob/main/data/clone-data.proof.json)
11
+ [![Git clones (14d)](https://img.shields.io/badge/git%20clones%2014d-115k-blueviolet?style=for-the-badge&logo=github)](https://github.com/ruvnet/ruflo/blob/main/data/clone-data.ledger.json)
12
+
9
13
  [![Star on GitHub](https://img.shields.io/github/stars/ruvnet/claude-flow?style=for-the-badge&logo=github&color=gold)](https://github.com/ruvnet/claude-flow)
10
14
  [![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](https://opensource.org/licenses/MIT)
11
15
  [![Claude Code](https://img.shields.io/badge/Claude%20Code-Plugin-D97757?style=for-the-badge&logoColor=white&logo=anthropic)](https://github.com/ruvnet/claude-flow)
12
16
  [![Codex Plugin](https://img.shields.io/badge/Codex-Plugin-412991?style=for-the-badge&logoColor=white&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI%2BPHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0yMi4yODIgOS44MjFhNS45ODUgNS45ODUgMCAwIDAtLjUxNi00LjkxIDYuMDQ2IDYuMDQ2IDAgMCAwLTYuNTEtMi45QTYuMDY1IDYuMDY1IDAgMCAwIDQuOTgxIDQuMThhNS45ODUgNS45ODUgMCAwIDAtMy45OTggMi45IDYuMDQ2IDYuMDQ2IDAgMCAwIC43NDMgNy4wOTcgNS45OCA1Ljk4IDAgMCAwIC41MSA0LjkxMSA2LjA1MSA2LjA1MSAwIDAgMCA2LjUxNSAyLjlBNS45ODUgNS45ODUgMCAwIDAgMTMuMjYgMjRhNi4wNTYgNi4wNTYgMCAwIDAgNS43NzItNC4yMDYgNS45OSA1Ljk5IDAgMCAwIDMuOTk4LTIuOSA2LjA1NiA2LjA1NiAwIDAgMC0uNzQ3LTcuMDczek0xMy4yNiAyMi40M2E0LjQ3NiA0LjQ3NiAwIDAgMS0yLjg3Ni0xLjA0bC4xNDItLjA4IDQuNzc4LTIuNzU4YS43OTUuNzk1IDAgMCAwIC4zOTMtLjY4MXYtNi43MzdsMi4wMiAxLjE2OGEuMDcxLjA3MSAwIDAgMSAuMDM4LjA1MnY1LjU4M2E0LjUwNCA0LjUwNCAwIDAgMS00LjQ5NSA0LjQ5NHpNMy42IDE4LjMwNGE0LjQ3IDQuNDcgMCAwIDEtLjUzNS0zLjAxNGwuMTQyLjA4NSA0Ljc4MyAyLjc1OWEuNzcxLjc3MSAwIDAgMCAuNzgxIDBsNS44NDMtMy4zNjl2Mi4zMzJhLjA4LjA4IDAgMCAxLS4wMzMuMDYyTDkuNzQgMTkuOTVhNC41IDQuNSAwIDAgMS02LjE0LTEuNjQ2ek0yLjM0IDcuODk2YTQuNDg1IDQuNDg1IDAgMCAxIDIuMzY2LTEuOTczVjExLjZhLjc2Ni43NjYgMCAwIDAgLjM4OC42NzdsNS44MTUgMy4zNTQtMi4wMiAxLjE2OGEuMDc2LjA3NiAwIDAgMS0uMDcyIDBsLTQuODMtMi43ODZBNC41MDQgNC41MDQgMCAwIDEgMi4zNCA3Ljg3MnptMTYuNTk3IDMuODU1LTUuODMzLTMuMzg3IDIuMDE2LTEuMTY1YS4wNzYuMDc2IDAgMCAxIC4wNzEgMGw0LjgzIDIuNzkxYTQuNDk0IDQuNDk0IDAgMCAxLS42NzYgOC4xMDR2LTUuNjc3YS43OS43OSAwIDAgMC0uNDA3LS42Njd6bTIuMDEtMy4wMjMtLjE0MS0uMDg1LTQuNzc0LTIuNzgyYS43NzYuNzc2IDAgMCAwLS43ODUgMEw5LjQwOSA5LjIzVjYuODk3YS4wNjYuMDY2IDAgMCAxIC4wMjgtLjA2Mmw0LjgzLTIuNzg3YTQuNDk5IDQuNDk5IDAgMCAxIDYuNjggNC42NnpNOC4zMDcgMTIuODYzbC0yLjAyLTEuMTY0YS4wOC4wOCAwIDAgMS0uMDM4LS4wNTdWNi4wNzRhNC40OTkgNC40OTkgMCAwIDEgNy4zNzYtMy40NTRsLS4xNDIuMDgtNC43NzggMi43NThhLjc5NS43OTUgMCAwIDAtLjM5My42ODJ6bTEuMDk3LTIuMzY2IDIuNjAyLTEuNSAyLjYwNyAxLjV2Mi45OTlsLTIuNTk3IDEuNS0yLjYwNy0xLjVaIi8%2BPC9zdmc%2B)](https://www.npmjs.com/package/@claude-flow/codex)
13
17
  [![🕸️ RuVector Graph Ai](https://img.shields.io/badge/RuVector_Agentic-DB-06b6d4?style=for-the-badge&logoColor=white&logo=graphql)](https://github.com/ruvnet/ruvector)
14
18
 
19
+ [![RuFlo Agentic Appliance](v3/docs/assets/RuFlo-agentic-appliance.png)](https://cognitum.one/appliance)
20
+
21
+ [![ruFlo Summit — Budapest, June 2–3, 2026](v3/docs/assets/ruFlo-Summit.jpg)](https://github.com/ruvnet/ruflo/issues/1967)
22
+
15
23
  # Ruflo
16
24
 
17
25
  **Multi-agent AI orchestration for Claude Code**
@@ -64,101 +72,108 @@ There are **two different install paths** with very different surface areas. Pic
64
72
  # Install core + any plugins you need
65
73
  /plugin install ruflo-core@ruflo
66
74
  /plugin install ruflo-swarm@ruflo
67
- /plugin install ruflo-autopilot@ruflo
68
- /plugin install ruflo-federation@ruflo
75
+ /plugin install ruflo-rag-memory@ruflo
76
+ /plugin install ruflo-neural-trader@ruflo
69
77
  ```
70
78
 
71
79
  This adds slash commands and agent definitions only. The Ruflo MCP server is NOT registered, so `memory_store`, `swarm_init`, `agent_spawn`, etc. won't be callable from Claude. For the full loop, use Path B below.
72
80
 
73
81
  <details>
74
- <summary><strong>🔌 All 32 plugins</strong></summary>
82
+ <summary><strong>🔌 All 33 plugins</strong></summary>
75
83
 
76
84
  #### Core & Orchestration
77
85
 
78
86
  | Plugin | What it does |
79
87
  |--------|-------------|
80
- | **ruflo-core** | Foundation — server, health checks, plugin discovery |
81
- | **ruflo-swarm** | Coordinate multiple agents as a team |
82
- | **ruflo-autopilot** | Let agents run autonomously in a loop |
83
- | **ruflo-loop-workers** | Schedule background tasks on a timer |
84
- | **ruflo-workflows** | Reusable multi-step task templates |
85
- | **ruflo-federation** | Agents on different machines collaborate securely |
88
+ | [**ruflo-core**](plugins/ruflo-core/README.md) | Foundation — server, health checks, plugin discovery |
89
+ | [**ruflo-swarm**](plugins/ruflo-swarm/README.md) | Coordinate multiple agents as a team |
90
+ | [**ruflo-autopilot**](plugins/ruflo-autopilot/README.md) | Let agents run autonomously in a loop |
91
+ | [**ruflo-loop-workers**](plugins/ruflo-loop-workers/README.md) | Schedule background tasks on a timer |
92
+ | [**ruflo-workflows**](plugins/ruflo-workflows/README.md) | Reusable multi-step task templates |
93
+ | [**ruflo-federation**](plugins/ruflo-federation/README.md) | Agents on different machines collaborate securely |
86
94
 
87
95
  #### Memory & Knowledge
88
96
 
89
97
  | Plugin | What it does |
90
98
  |--------|-------------|
91
- | **ruflo-agentdb** | Fast vector database for agent memory |
92
- | **ruflo-rag-memory** | Smart retrieval — hybrid search, graph hops, diversity ranking |
93
- | **ruflo-rvf** | Save and restore agent memory across sessions |
94
- | **ruflo-ruvector** | [`ruvector`](https://npmjs.com/package/ruvector) — GPU-accelerated search, Graph RAG, 103 tools |
95
- | **ruflo-knowledge-graph** | Build and traverse entity relationship maps |
99
+ | [**ruflo-agentdb**](plugins/ruflo-agentdb/README.md) | Fast vector database for agent memory |
100
+ | [**ruflo-rag-memory**](plugins/ruflo-rag-memory/README.md) | Smart retrieval — hybrid search, graph hops, diversity ranking |
101
+ | [**ruflo-rvf**](plugins/ruflo-rvf/README.md) | Save and restore agent memory across sessions |
102
+ | [**ruflo-ruvector**](plugins/ruflo-ruvector/README.md) | [`ruvector`](https://npmjs.com/package/ruvector) — GPU-accelerated search, Graph RAG, 103 tools |
103
+ | [**ruflo-knowledge-graph**](plugins/ruflo-knowledge-graph/README.md) | Build and traverse entity relationship maps |
96
104
 
97
105
  #### Intelligence & Learning
98
106
 
99
107
  | Plugin | What it does |
100
108
  |--------|-------------|
101
- | **ruflo-intelligence** | Agents learn from past successes and get smarter |
102
- | **ruflo-daa** | Dynamic agent behavior and cognitive patterns |
103
- | **ruflo-ruvllm** | Run local LLMs (Ollama, etc.) with smart routing |
104
- | **ruflo-goals** | Break big goals into plans and track progress |
109
+ | [**ruflo-intelligence**](plugins/ruflo-intelligence/README.md) | Agents learn from past successes and get smarter |
110
+ | [**ruflo-graph-intelligence**](plugins/ruflo-graph-intelligence/) | Sublinear graph reasoning PageRank, delta updates, complexity-aware execution (ADR-123) |
111
+ | [**ruflo-daa**](plugins/ruflo-daa/README.md) | Dynamic agent behavior and cognitive patterns |
112
+ | [**ruflo-ruvllm**](plugins/ruflo-ruvllm/README.md) | Run local LLMs (Ollama, etc.) with smart routing |
113
+ | [**ruflo-goals**](plugins/ruflo-goals/README.md) | Break big goals into plans and track progress |
105
114
 
106
115
  #### Code Quality & Testing
107
116
 
108
117
  | Plugin | What it does |
109
118
  |--------|-------------|
110
- | **ruflo-testgen** | Find missing tests and generate them automatically |
111
- | **ruflo-browser** | Automate browser testing with Playwright |
112
- | **ruflo-jujutsu** | Analyze git diffs, score risk, suggest reviewers |
113
- | **ruflo-docs** | Generate and maintain documentation automatically |
119
+ | [**ruflo-testgen**](plugins/ruflo-testgen/README.md) | Find missing tests and generate them automatically |
120
+ | [**ruflo-browser**](plugins/ruflo-browser/README.md) | Automate browser testing with Playwright |
121
+ | [**ruflo-jujutsu**](plugins/ruflo-jujutsu/README.md) | Analyze git diffs, score risk, suggest reviewers |
122
+ | [**ruflo-docs**](plugins/ruflo-docs/README.md) | Generate and maintain documentation automatically |
114
123
 
115
124
  #### Security & Compliance
116
125
 
117
126
  | Plugin | What it does |
118
127
  |--------|-------------|
119
- | **ruflo-security-audit** | Scan for vulnerabilities and CVEs |
120
- | **ruflo-aidefence** | Block prompt injection, detect PII, safety scanning |
128
+ | [**ruflo-security-audit**](plugins/ruflo-security-audit/README.md) | Scan for vulnerabilities and CVEs |
129
+ | [**ruflo-aidefence**](plugins/ruflo-aidefence/README.md) | Block prompt injection, detect PII, safety scanning |
121
130
 
122
131
  #### Architecture & Methodology
123
132
 
124
133
  | Plugin | What it does |
125
134
  |--------|-------------|
126
- | **ruflo-adr** | Track architecture decisions with a living record |
127
- | **ruflo-ddd** | Scaffold domain-driven design — contexts, aggregates, events |
128
- | **ruflo-sparc** | Guided 5-phase development methodology with quality gates |
135
+ | [**ruflo-adr**](plugins/ruflo-adr/README.md) | Track architecture decisions with a living record |
136
+ | [**ruflo-ddd**](plugins/ruflo-ddd/README.md) | Scaffold domain-driven design — contexts, aggregates, events |
137
+ | [**ruflo-sparc**](plugins/ruflo-sparc/README.md) | Guided 5-phase development methodology with quality gates |
129
138
 
130
139
  #### DevOps & Observability
131
140
 
132
141
  | Plugin | What it does |
133
142
  |--------|-------------|
134
- | **ruflo-migrations** | Manage database schema changes safely |
135
- | **ruflo-observability** | Structured logs, traces, and metrics in one place |
136
- | **ruflo-cost-tracker** | Track token usage, set budgets, get cost alerts |
143
+ | [**ruflo-migrations**](plugins/ruflo-migrations/README.md) | Manage database schema changes safely |
144
+ | [**ruflo-observability**](plugins/ruflo-observability/README.md) | Structured logs, traces, and metrics in one place |
145
+ | [**ruflo-cost-tracker**](plugins/ruflo-cost-tracker/README.md) | Track token usage, set budgets, get cost alerts |
137
146
 
138
147
  #### Extensibility
139
148
 
140
149
  | Plugin | What it does |
141
150
  |--------|-------------|
142
- | **ruflo-wasm** | Run sandboxed WebAssembly agents |
143
- | **ruflo-plugin-creator** | Scaffold, validate, and publish your own plugins |
151
+ | [**ruflo-agent**](plugins/ruflo-agent/README.md) | Run agents local WASM sandbox (rvagent) + Anthropic Claude Managed Agents (cloud) |
152
+ | [**ruflo-plugin-creator**](plugins/ruflo-plugin-creator/README.md) | Scaffold, validate, and publish your own plugins |
144
153
 
145
154
  #### Domain-Specific
146
155
 
147
156
  | Plugin | What it does |
148
157
  |--------|-------------|
149
- | **ruflo-iot-cognitum** | IoT device management — trust scoring, anomaly detection, fleets |
150
- | **ruflo-neural-trader** | [`neural-trader`](https://npmjs.com/package/neural-trader) — AI trading with 4 agents, backtesting, 112+ tools |
151
- | **ruflo-market-data** | Ingest market data, vectorize OHLCV, detect patterns |
158
+ | [**ruflo-iot-cognitum**](plugins/ruflo-iot-cognitum/README.md) | IoT device management — trust scoring, anomaly detection, fleets |
159
+ | [**ruflo-neural-trader**](plugins/ruflo-neural-trader/README.md) | [`neural-trader`](https://npmjs.com/package/neural-trader) — AI trading with 4 agents, backtesting, 112+ tools |
160
+ | [**ruflo-market-data**](plugins/ruflo-market-data/README.md) | Ingest market data, vectorize OHLCV, detect patterns |
152
161
 
153
162
  </details>
154
163
 
155
164
  ### CLI Install
156
165
 
166
+ **macOS / Linux / WSL / Git-Bash:**
167
+
157
168
  ```bash
158
- # One-line install
169
+ # One-line install (POSIX shells only — see Windows note below)
159
170
  curl -fsSL https://cdn.jsdelivr.net/gh/ruvnet/ruflo@main/scripts/install.sh | bash
171
+ ```
172
+
173
+ **All platforms (including native Windows PowerShell / cmd):**
160
174
 
161
- # Or via npx (interactive setup)
175
+ ```bash
176
+ # Interactive setup wizard — runs identically on every platform
162
177
  npx ruflo@latest init wizard
163
178
 
164
179
  # Quick non-interactive init
@@ -168,6 +183,8 @@ npx ruflo@latest init wizard
168
183
  npm install -g ruflo@latest
169
184
  ```
170
185
 
186
+ > 💡 **Windows users:** the `curl ... | bash` form needs a POSIX shell (Git-Bash, WSL, MSYS). The `npx ruflo@latest init wizard` line works natively in PowerShell and cmd. If you hit an `'bash' is not recognized` error, use the `npx` line instead — both end up running the same init flow.
187
+
171
188
  ### MCP Server
172
189
 
173
190
  ```bash
@@ -263,6 +280,8 @@ The difference: some channels are trusted, some aren't. [`@claude-flow/plugin-ag
263
280
 
264
281
  You don't configure handshakes or manage certificates. You `federation init`, `federation join`, and your agents start talking. The protocol handles identity, the PII pipeline handles data safety, and the audit trail handles compliance.
265
282
 
283
+ > **📘 Full user guide:** [`docs/federation/`](./docs/federation/) — setup, MCP tools, trust levels, circuit breaker, and the (opt-in) WireGuard mesh layer that ties packet-layer reachability to federation trust. ADR-111 deep-dive at [`docs/federation/phase7-mesh-bringup.md`](./docs/federation/phase7-mesh-bringup.md).
284
+
266
285
  <details>
267
286
  <summary><strong>Federation capabilities</strong></summary>
268
287
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruflo",
3
- "version": "3.7.0-alpha.9",
3
+ "version": "3.8.0",
4
4
  "description": "Ruflo - Enterprise AI agent orchestration platform. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "bin/ruflo.js",
6
6
  "type": "module",
@@ -40,7 +40,7 @@
40
40
  "package:rvf": "bash src/scripts/package-rvf.sh"
41
41
  },
42
42
  "dependencies": {
43
- "@claude-flow/cli": "^3.7.0-alpha.1"
43
+ "@claude-flow/cli": "^3.7.0-alpha.11"
44
44
  },
45
45
  "overrides": {
46
46
  "@ruvector/rvf-wasm": "0.1.5",
@@ -56,7 +56,19 @@
56
56
  "make-fetch-happen": ">=15.0.0",
57
57
  "express-rate-limit": ">=8.4.1",
58
58
  "protobufjs": ">=7.5.5",
59
- "uuid": ">=14.0.0"
59
+ "uuid": ">=14.0.0",
60
+ "@opentelemetry/core": "1.25.1",
61
+ "@opentelemetry/resources": "1.25.1",
62
+ "@opentelemetry/sdk-trace-base": "1.25.1",
63
+ "@opentelemetry/sdk-node": ">=0.218.0",
64
+ "@opentelemetry/auto-instrumentations-node": ">=0.75.0",
65
+ "@opentelemetry/exporter-prometheus": ">=0.217.0",
66
+ "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1",
67
+ "@opentelemetry/exporter-trace-otlp-http": "0.52.1",
68
+ "@opentelemetry/exporter-trace-otlp-proto": "0.52.1",
69
+ "@opentelemetry/otlp-exporter-base": "0.52.1",
70
+ "@opentelemetry/otlp-grpc-exporter-base": "0.52.1",
71
+ "@opentelemetry/otlp-transformer": "0.52.1"
60
72
  },
61
73
  "engines": {
62
74
  "node": ">=20.0.0"
@@ -256,7 +256,7 @@ const BACKEND_DEFS = [
256
256
  { name: "agentic-flow", command: "npx", args: ["-y", "agentic-flow@alpha", "mcp", "start"], groups: ["agentic-flow"] },
257
257
  { name: "claude", command: "claude", args: ["mcp", "serve"], groups: ["claude-code"] },
258
258
  { name: "gemini-mcp", command: "npx", args: ["-y", "gemini-mcp-server"], groups: ["gemini"] },
259
- { name: "codex", command: "npx", args: ["-y", "@openai/codex", "mcp", "serve"], groups: ["codex"] },
259
+ { name: "codex", command: "npx", args: ["-y", "@openai/codex", "mcp-server"], groups: ["codex"] },
260
260
  ];
261
261
 
262
262
  const mcpBackends = new Map();
@@ -639,11 +639,35 @@ Requires: OPENAI_API_KEY environment variable (already set for OpenAI models).
639
639
  return { guidance: `Unknown topic '${topic}'. Use 'overview', 'groups', or a specific group name.`, topic };
640
640
  }
641
641
 
642
+ // =============================================================================
643
+ // SSRF GUARD — Reject requests to private/loopback ranges (CWE-918)
644
+ // =============================================================================
645
+
646
+ const PRIVATE_IP_RE = /^(?:10\.|172\.(?:1[6-9]|2\d|3[01])\.|192\.168\.|127\.|0\.|::1|fc|fd)/i;
647
+
648
+ function assertSafeUrl(rawUrl) {
649
+ let parsed;
650
+ try {
651
+ parsed = new URL(rawUrl);
652
+ } catch {
653
+ throw new Error(`SSRF guard: invalid URL — ${rawUrl}`);
654
+ }
655
+ if (parsed.protocol !== "https:") {
656
+ throw new Error(`SSRF guard: only HTTPS URLs are permitted, got ${parsed.protocol}`);
657
+ }
658
+ const host = parsed.hostname;
659
+ if (PRIVATE_IP_RE.test(host) || host === "localhost" || host.endsWith(".local")) {
660
+ throw new Error(`SSRF guard: private/loopback host rejected — ${host}`);
661
+ }
662
+ }
663
+
642
664
  // =============================================================================
643
665
  // HELPER — Call a backend Cloud Function / API
644
666
  // =============================================================================
645
667
 
646
668
  async function callCloudFunction(url, payload, timeoutMs = 25000) {
669
+ // Validate the URL before making any network request.
670
+ assertSafeUrl(url);
647
671
  const controller = new AbortController();
648
672
  const timer = setTimeout(() => controller.abort(), timeoutMs);
649
673
  try {
@@ -301,5 +301,5 @@ INSERT OR REPLACE INTO metadata (key, value) VALUES
301
301
 
302
302
  -- Create default vector index configuration
303
303
  INSERT OR IGNORE INTO vector_indexes (id, name, dimensions) VALUES
304
- ('default', 'default', 768),
305
- ('patterns', 'patterns', 768);
304
+ ('default', 'default', 384),
305
+ ('patterns', 'patterns', 384);
@@ -261,7 +261,7 @@ const BACKEND_DEFS = [
261
261
  { name: "agentic-flow", command: "npx", args: ["-y", "agentic-flow@alpha", "mcp", "start"], groups: ["agentic-flow"] },
262
262
  { name: "claude", command: "claude", args: ["mcp", "serve"], groups: ["claude-code"] },
263
263
  { name: "gemini-mcp", command: "npx", args: ["-y", "gemini-mcp-server"], groups: ["gemini"] },
264
- { name: "codex", command: "npx", args: ["-y", "@openai/codex", "mcp", "serve"], groups: ["codex"] },
264
+ { name: "codex", command: "npx", args: ["-y", "@openai/codex", "mcp-server"], groups: ["codex"] },
265
265
  ];
266
266
 
267
267
  const mcpBackends = new Map();
@@ -728,11 +728,35 @@ async function geminiGroundedSearch(query, mode = "search") {
728
728
  }
729
729
  }
730
730
 
731
+ // =============================================================================
732
+ // SSRF GUARD — Reject requests to private/loopback ranges (CWE-918)
733
+ // =============================================================================
734
+
735
+ const PRIVATE_IP_RE = /^(?:10\.|172\.(?:1[6-9]|2\d|3[01])\.|192\.168\.|127\.|0\.|::1|fc|fd)/i;
736
+
737
+ function assertSafeUrl(rawUrl) {
738
+ let parsed;
739
+ try {
740
+ parsed = new URL(rawUrl);
741
+ } catch {
742
+ throw new Error(`SSRF guard: invalid URL — ${rawUrl}`);
743
+ }
744
+ if (parsed.protocol !== "https:") {
745
+ throw new Error(`SSRF guard: only HTTPS URLs are permitted, got ${parsed.protocol}`);
746
+ }
747
+ const host = parsed.hostname;
748
+ if (PRIVATE_IP_RE.test(host) || host === "localhost" || host.endsWith(".local")) {
749
+ throw new Error(`SSRF guard: private/loopback host rejected — ${host}`);
750
+ }
751
+ }
752
+
731
753
  // =============================================================================
732
754
  // HELPER — Call a backend Cloud Function / API
733
755
  // =============================================================================
734
756
 
735
757
  async function callCloudFunction(url, payload, timeoutMs = 25000) {
758
+ // Validate the URL before making any network request.
759
+ assertSafeUrl(url);
736
760
  const controller = new AbortController();
737
761
  const timer = setTimeout(() => controller.abort(), timeoutMs);
738
762
  try {