Skip to content

add services - #26

Open
alexcos20 wants to merge 1 commit into
mainfrom
feature/services
Open

add services#26
alexcos20 wants to merge 1 commit into
mainfrom
feature/services

Conversation

@alexcos20

@alexcos20 alexcos20 commented Jul 29, 2026

Copy link
Copy Markdown
Member

Fixes #24

Service-on-Demand: rent long-running containers on an Ocean node

Summary

Adds the Service-on-Demand surface to on-mcp: 11 tools covering the full lifecycle of a long-running, pre-paid container on an Ocean node (discover → estimate → provision escrow → start → poll → operate → stop), plus a run_service prompt, an ocean://docs/service-on-demand resource, and 8 new NodeClient wrappers over the SERVICE_* protocol commands. Requires @oceanprotocol/lib 9.0.0-next.8 (up from ^8.2.0), which is where the service commands land.

A service is not a compute job: it does not run to completion, it stays up for a paid duration and exposes network endpoints the consumer connects to. That difference drives most of the design below.

Why

Ocean nodes gained Service-on-Demand (ocean-node #1408) but nothing exposed it to agents. The lifecycle has several traps that an agent gets wrong by default, and each one costs the user real money, so the tools are shaped to make the traps unreachable rather than merely documented:

  • Start is asynchronous. serviceStart returns a serviceId at Starting (10); escrow lock → image pull/build + vulnerability scan → claim → port allocation → container start all happen in a background loop. An agent that reports "started" is wrong.
  • There is no server-side quote. Compute has initializeCompute; services have no equivalent command, so the cost has to be estimated client-side against the node's own arithmetic.
  • The whole window is paid up front and the reservation outlives the container. serviceStop tears the container down but keeps cpu/ram/gpu and host ports reserved until expiresAt. Stopping early neither refunds nor frees capacity — an agent offering it as a cost saving is actively harmful.
  • Stopping (50) is not an end state and Error (99) still holds the paid reservation and is restartable.

What changed

New: 11 service tools — src/tools/serviceTools.ts

Tool Auth Notes
findServiceEnvironments none envs advertised service-capable, with fee schedule + free capacity per requested resource
findServiceNodes none bounded fan-out (default 12 peers) over list_discovered_peers; unreachable peers are skipped, not fatal; reports probedCount and what it truncated
getServiceTemplates none operator catalogue + paste-ready serviceStartArgs projection
estimateServiceCost none client-side estimate + ready-made payment object
serviceStart yes escrow-gated; asynchronous
serviceStatus yes owner-scoped; optional bounded waitForRunning
getServices yes node-wide, not owner-scoped — you see other consumers' services
serviceExtend yes escrow-gated; bills the additional window alone
serviceRestart yes REUSE or RESPEC, validated locally
serviceStop yes keeps the reservation until expiresAt
serviceLogs yes bounded by default

Design decisions worth reviewing:

  • No templateId on serviceStart. The node has no such parameter — templates are a suggestion, not an allow-list, and the caller always passes the container spec explicitly. getServiceTemplates therefore emits a serviceStartArgs projection per entry, pre-renamed (template.commanddockerCmd, template.entrypointdockerEntrypoint), because those fields copied verbatim are silently dropped and the container runs its image default.
  • An empty template catalogue is normal (the node just reads a folder) and is never reported as "services unavailable".
  • Two env-var families are kept apart: userDataKeys (yours, fillable via userData) vs operatorEnvVarKeys (the operator's — keys exposed, values never).
  • Pre-flight guards that fail locally instead of after payment: in-container ports < 1024 are rejected up front (the container runs CapDrop: ['ALL'], so it cannot bind them — it would start, be charged for, and never serve); mutually exclusive tag/checksum/dockerfile; features.services === false envs; and a RESPEC guard that catches "one new dockerCmd" before the node's 400.
  • serviceLogs is bounded by defaultsince: '5m', 256 KiB cap, truncated: true rather than a huge blob. Unbounded, the node streams the container's entire history.
  • userData values never appear in a response. Tools echo keys only; the node strips values too, so a rotation is a full RESPEC restart (documented with a recipe).

New: cost + decoration helpers — src/tools/serviceCost.ts, src/tools/serviceSchemas.ts

Pure, network-free, unit-tested. Ported from ocean-cli's serviceHelpers.ts with the divergences from ocean-node's own arithmetic corrected:

  • estimateServiceCost implements the node's formula — price(id) × amount × ceil(effectiveDuration / 60) with effectiveDuration = max(duration, env.minJobDuration) — matches feeToken case-insensitively but echoes it back verbatim as advertised, and surfaces unpricedResourceIds because the node prices an unknown resource id at 0 silently (a typo'd resource looks free).
  • toRawAmount emits a decimal string: an 18-decimal amount exceeds Number.MAX_SAFE_INTEGER.
  • Zero cost ⇒ escrowRequired: false and no payment, instead of an invalid payment object.
  • decorateServiceJob adds what the node does not send: statusLabel (covering 45 Restarting), expiresAtIso, isTerminal, paymentClaimed (a precondition for restart), and warnings for the two money-relevant statuses — Stopping (50) is in flight and still holds resources; Error (99) is terminal but restartable with the reservation intact.

New: serviceEscrowGate on the two paying tools

serviceStart and serviceExtend reuse the existing escrow_preflight core: estimate → build payment → runEscrowPreflight → refuse with escrow_preflight_failed when escrow cannot back the service. This matters more than for compute — a doomed service start still returns a serviceId and then dies asynchronously at Locking, which is strictly worse than a synchronous refusal. Best-effort throughout: no evmRegistry, an unresolvable payer, a free service or any internal failure falls through and lets the node decide. skipEscrowPreflight bypasses.

src/tools/escrowPreflight.ts gains serviceMinLockSeconds(d) — a padded lower bound, not d + 3600. The node's rule is d + claimDurationTimeout, and claimDurationTimeout is per-node config no protocol command exposes, so an exact-3600 figure would green-light a service whose createLock then fails with nothing to explain why.

New: 8 NodeClient wrappers — src/clients/nodeClient.ts

Thin wrappers like the rest of the class, with two deliberate asymmetries flagged in comments so nobody "fixes" them: serviceStart and serviceRestart route via ProviderInstance (BaseProvider) rather than getP2p(), because only BaseProvider carries the notifyIncentiveBackendService* hooks. getImpl() dispatches a NodeP2P target to the same P2pProvider, so wire behaviour is identical; the hooks are no-ops unless INCENTIVE_BACKEND_URL is set (we do not set it), but the call sites are correct if a deployment ever does.

Also refactors the compute-result/compute-log collectors onto a shared collectStream(raw, maxBytes) so service logs can cap collection.

Fixed: auth-token signing under lib v9 — src/tools/p2pSchemas.ts

The signed message gained a fourth component: consumerAddress + nonce + protocolCommand + issuerPeerId. issuerPeerId is empty for every command except CREATE_AUTH_TOKEN, where it is the target node's peerId — so only token minting was affected, and the previously documented string produced a signature the node rejects. P2P_AUTH_SIGNING_GUIDE now documents this, lists the 8 service commands with their PROTOCOL_COMMANDS values, and createAuthToken accepts the new optional validUntil.

P2P_RECOMMENDED_NODES_GUIDE also now says explicitly not to use the recommended-nodes list or find_provider for services: capability is per-environment (features.services) with no DHT advertise string, so discovery is a fan-out.

New: docs resource + prompt

  • ocean://docs/service-on-demand (src/utils/serviceOnDemand.ts) — the services mental model, status table, money semantics, safety surface, and recommended flow. Registered in the resource catalog and listed in the README.
  • run_service prompt — goal + optional node peerID + duration → discover, estimate, provision escrow, start, poll to Running without pestering the user between polls, hand over endpoints with the "endpoints are not node-authenticated" caveat.

Removed: debug_node and the DIAGNOSTICS table

on-mcp is a remote server talking to remote p2p ocean-nodes. Shipping docker logs ocean-node, ss -tlnp, nvidia-smi and cat /etc/docker/daemon.json checklists to a client that cannot run them was wrong, so the 9-entry DIAGNOSTICS table and the prompt it fed are gone (−183 lines). Node-operator guidance stays where it already lived and does not assume shell access: get_workflow("troubleshoot_node"), check_node_eligibility, search_docs.

Notes for reviewers

  • features.services is advisory and stays that way. It defaults to true node-side and the start path never re-checks that serviceOnDemand was configured, so neither the flag nor an empty template list proves a node is provisioned. Env eligibility is a strong filter; SERVICE_START is authoritative. No upstream change requested.
  • estimateServiceCost is an estimate, and the tool descriptions say so repeatedly. Beyond the unpriced-id trap: serviceOnDemand.maxDurationSeconds (default 86400) is not advertised, so an over-long duration fails at start with a 400.
  • Same cost model as compute, verified. calculateResourcesCost in ocean-node's compute_engine_base.ts is one shared method called by initializeCompute, startCompute, startService and extendService, so nothing had to change on the compute side. One known client-side divergence remains, services-only and false-negative-safe: our availableFor subtracts inUse for every resource kind, while the node applies the per-env ceiling to fungible only and never blocks allocation on shareable discrete resources. That can make findServiceEnvironments drop an env the node would accept. Not fixed here.
  • getServices being node-wide is a footgun — the description leads with it, since presenting another consumer's services as the user's own would be a privacy problem.

Summary by CodeRabbit

  • New Features

    • Added Service-on-Demand tools for discovering environments and templates, estimating costs, and starting, monitoring, extending, restarting, stopping, and listing long-running services.
    • Added service status polling, endpoint availability checks, bounded log retrieval, payment reservation handling, and safer secret/user-data reporting.
    • Added a run_service guided workflow for launching services.
  • Documentation

    • Added comprehensive Service-on-Demand lifecycle, payment, authentication, restart, and security guidance through MCP documentation resources.

@alexcos20
alexcos20 requested a review from bogdanfazakas July 29, 2026 12:49
@alexcos20 alexcos20 self-assigned this Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Service-on-Demand support across MCP schemas, cost estimation, escrow preflight, node lifecycle wrappers, tool registration, documentation, prompts, and unit/integration tests.

Changes

Service-on-Demand

Layer / File(s) Summary
Service contracts and documentation
src/tools/serviceSchemas.ts, src/utils/serviceOnDemand.ts, src/resources/resourceCatalog.ts, src/prompts/registerPrompts.ts, README.md, src/tools/p2pSchemas.ts, package.json
Adds shared schemas, lifecycle guides, documentation resources, a run_service prompt, authentication guidance, and the updated Ocean library dependency.
Cost, escrow, and node client flow
src/tools/serviceCost.ts, src/tools/escrowPreflight.ts, src/clients/nodeClient.ts
Adds resource filtering, cost and raw-token calculations, payment construction, escrow lock padding, stream collection, authentication validity, and Service-on-Demand node operations.
MCP service orchestration
src/tools/serviceTools.ts, src/tools/registerTools.ts
Registers discovery, estimation, start, status, listing, extension, restart, stop, log, and escrow-gating behavior.
Service behavior validation
src/test/unit/tools/*, src/test/integration/services.test.ts, src/test/integration/docsMerge.test.ts
Covers schemas, cost and payment behavior, templates, registration metadata, escrow gating, documentation registration, and the asynchronous service lifecycle.

Estimated code review effort: 5 (Critical) | ~90 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant MCPTools
  participant NodeClient
  participant OceanNode
  participant EVMRegistry
  Client->>MCPTools: estimateServiceCost
  MCPTools->>OceanNode: inspect environments and fee schedules
  MCPTools->>EVMRegistry: read token decimals
  MCPTools-->>Client: cost and escrow payment data
  Client->>MCPTools: serviceStart
  MCPTools->>NodeClient: run escrow gate and start request
  NodeClient->>OceanNode: start service
  OceanNode-->>Client: Starting service record
  Client->>MCPTools: serviceStatus polling
  MCPTools->>NodeClient: query service status
  NodeClient->>OceanNode: fetch lifecycle state
  OceanNode-->>Client: Running service and endpoints
Loading

Possibly related PRs

  • oceanprotocol/on-mcp#18: Introduces the escrow preflight readiness flow used by the Service-on-Demand start and extension paths.

Suggested reviewers: bogdanfazakas

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too generic and does not clearly identify the Service-on-Demand MCP changes. Use a specific title such as "Add Service-on-Demand support" or similar to reflect the main change.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/services

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@alexcos20

Copy link
Copy Markdown
Member Author

/run-security-scan

@alexcos20 alexcos20 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI automated code review (Gemini 3).

Overall risk: low

Summary:
Excellent implementation of Service-on-Demand functionality. The code demonstrates a high level of care for edge cases (e.g., float64 arithmetic in token conversion, chunked stream limiting, and accurate MCP schema descriptions). Minor drift found in a code comment, but the logic itself is flawless. LGTM!

Comments:
• [INFO][style] The comment mentions 45 Restarting as 'settled enough to report', but the settled function directly below it explicitly does NOT include 45 (it only checks for 40 and terminal statuses). The code logic is correct since 45 Restarting is an in-flight status and should continue polling (as defined in your SERVICE_POLLING_GUIDE), so just the comment needs a tiny tweak to remove the mention of 45 being settled.

-          // 40 Running and 45 Restarting are both "settled enough to report"; anything
+          // 40 Running is "settled enough to report"; anything
           // terminal ends the wait too. 50 Stopping is in flight, so it keeps waiting.

• [INFO][other] Excellent handling of JavaScript float64 to exact decimal string conversion here. Manually expanding the scientific notation output of toPrecision ensures that no binary-representation noise leaks into the raw base units for escrow preflight. Great job avoiding the typical parseUnits pitfalls.
• [INFO][performance] This collectStream implementation cleanly and safely handles chunk concatenation with truncation. Bounding the memory growth when streaming large service logs (which can be days long) is an excellent defensive practice.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/test/integration/services.test.ts (2)

78-84: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

last.status !== 40 is dead. isServiceTerminal(40) is false (asserted in serviceStatusLabels.test.ts), so the extra clause never affects the branch and reads as if Running were terminal.

♻️ Simplify
-        if (isServiceTerminal(last.status) && last.status !== 40) {
+        if (isServiceTerminal(last.status)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/integration/services.test.ts` around lines 78 - 84, Remove the
redundant last.status !== 40 condition from the terminal-failure check in the
service-waiting logic, leaving isServiceTerminal(last.status) as the sole
predicate while preserving the existing error message and fail-fast behavior.

93-116: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No teardown: a failure mid-suite leaves a paid service running. If reaches Running/logs/extend throws, the final stop test never runs and the escrow-funded service stays up until expiry. There is also no counterpart teardown for ProviderInstance.setupP2P, which can keep mocha alive.

♻️ Suggested best-effort cleanup hook
   it('lists templates without treating an empty catalogue as a failure', async () => {

Add after the before hook:

after(async () => {
  if (!serviceId) return
  try {
    await nodeClient.serviceStop(node, AUTH_TOKEN!, serviceId, TIMEOUT_MS)
  } catch {
    // best-effort: the service may already be stopped or expired
  }
})

Also applies to: 235-243

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/integration/services.test.ts` around lines 93 - 116, Add an after
teardown hook alongside the existing before setup in the integration suite: when
serviceId exists, best-effort call nodeClient.serviceStop with node, AUTH_TOKEN,
serviceId, and TIMEOUT_MS, swallowing cleanup errors so already-stopped or
expired services do not fail teardown. Also clean up the P2P resources
initialized by ProviderInstance.setupP2P using the existing provider teardown
API.
src/tools/serviceTools.ts (1)

871-889: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

waitForRunning is silently ignored when serviceId is omitted. The response still reports waitedSeconds: 0, waitTimedOut: false, which reads as "settled" rather than "never waited". Consider surfacing a note (or rejecting the combination) so an agent doesn't conclude the service is up.

♻️ Suggested handling
-        if (args.waitForRunning && args.serviceId) {
+        if (args.waitForRunning && !args.serviceId) {
+          return errorPayload(
+            'waitForRunning requires a serviceId — there is no single job to wait on otherwise.'
+          )
+        }
+        if (args.waitForRunning && args.serviceId) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/serviceTools.ts` around lines 871 - 889, Handle the case where
waitForRunning is true but serviceId is absent instead of silently skipping the
wait logic. Update the response state around waitedSeconds and waitTimedOut to
surface that no service was selected for waiting, or reject the invalid
combination, while preserving the existing polling behavior when serviceId is
provided.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/tools/serviceCost.ts`:
- Around line 292-335: Harden parseUserData against ReDoS from untrusted
spec.validation patterns before invoking re.test(value). Add a bounded, safe
validation approach that rejects overly complex or unsafe regex patterns, or
evaluates them outside the main thread with a timeout; preserve invalid-pattern
warnings and existing mismatch errors for accepted patterns.

In `@src/tools/serviceTools.ts`:
- Line 696: Add a required-image validation guard in serviceStart beside the
existing image-mode/port checks, before serviceEscrowGate is called. Reject
requests when the start image is omitted or undefined, rather than allowing the
optional serviceContainerSpecSchema.image value to be cast and forwarded to the
node.

---

Nitpick comments:
In `@src/test/integration/services.test.ts`:
- Around line 78-84: Remove the redundant last.status !== 40 condition from the
terminal-failure check in the service-waiting logic, leaving
isServiceTerminal(last.status) as the sole predicate while preserving the
existing error message and fail-fast behavior.
- Around line 93-116: Add an after teardown hook alongside the existing before
setup in the integration suite: when serviceId exists, best-effort call
nodeClient.serviceStop with node, AUTH_TOKEN, serviceId, and TIMEOUT_MS,
swallowing cleanup errors so already-stopped or expired services do not fail
teardown. Also clean up the P2P resources initialized by
ProviderInstance.setupP2P using the existing provider teardown API.

In `@src/tools/serviceTools.ts`:
- Around line 871-889: Handle the case where waitForRunning is true but
serviceId is absent instead of silently skipping the wait logic. Update the
response state around waitedSeconds and waitTimedOut to surface that no service
was selected for waiting, or reject the invalid combination, while preserving
the existing polling behavior when serviceId is provided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 02f6723d-d4df-479b-baef-9c1f560b6fb7

📥 Commits

Reviewing files that changed from the base of the PR and between 28bf0b9 and 8398b2d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • README.md
  • package.json
  • src/clients/nodeClient.ts
  • src/prompts/registerPrompts.ts
  • src/resources/resourceCatalog.ts
  • src/test/integration/docsMerge.test.ts
  • src/test/integration/services.test.ts
  • src/test/unit/tools/registerTools.test.ts
  • src/test/unit/tools/serviceCost.test.ts
  • src/test/unit/tools/serviceStatusLabels.test.ts
  • src/test/unit/tools/serviceTemplates.test.ts
  • src/test/unit/tools/serviceTools.test.ts
  • src/tools/escrowPreflight.ts
  • src/tools/p2pSchemas.ts
  • src/tools/registerTools.ts
  • src/tools/serviceCost.ts
  • src/tools/serviceSchemas.ts
  • src/tools/serviceTools.ts
  • src/utils/serviceOnDemand.ts

Comment thread src/tools/serviceCost.ts
Comment on lines +292 to +335
export function parseUserData(
data: Record<string, unknown> | undefined,
userConfigurableEnvVars?: UserConfigurableEnvVar[]
): { data?: Record<string, unknown>; warnings: string[] } {
if (!data) return { warnings: [] }
if (typeof data !== 'object' || Array.isArray(data)) {
throw new Error('userData must be a JSON object (not an array or primitive)')
}

const warnings: string[] = []
const byKey = new Map((userConfigurableEnvVars ?? []).map((v) => [v.key, v]))

for (const key of Object.keys(data)) {
const spec = byKey.get(key)
if (!spec) {
if (userConfigurableEnvVars?.length) {
warnings.push(
`userData key "${key}" is not listed in the template's userConfigurableEnvVars — ` +
`sent anyway (templates are suggestions, not allow-lists).`
)
}
continue
}
if (!spec.validation) continue
let re: RegExp | undefined
try {
re = new RegExp(spec.validation)
} catch {
warnings.push(
`Template validation pattern for "${key}" is not a valid regex — skipped.`
)
continue
}
const value = data[key]
if (typeof value === 'string' && !re.test(value)) {
throw new Error(
`userData value for "${key}" does not match the template's validation pattern ` +
`(${spec.validation}). The value is not shown.`
)
}
}

return { data, warnings }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

ReDoS risk: template-supplied regex run against consumer input.

spec.validation originates from a remote node operator's getServiceTemplates() response (an arbitrary, untrusted peer reachable via findServiceNodes), and new RegExp(spec.validation) is then executed synchronously via re.test(value) against consumer-controlled userData. A malicious or compromised operator can publish a catastrophic-backtracking pattern in a template and hang the whole (single-threaded) MCP server process for any consumer that supplies a matching value — this is exactly the CWE-1333 ReDoS shape both linters flagged.

Consider bounding pattern complexity (e.g. reject overly long/nested patterns, or vet with a safe-regex checker) or running .test() with an execution timeout (e.g. via a worker) before trusting operator-supplied patterns.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 317-317: Do not use variable for regular expressions
Context: new RegExp(spec.validation)
Note: [CWE-1333] Inefficient Regular Expression Complexity. Security best practice.

(regexp-non-literal-typescript)


[warning] 317-317: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(spec.validation)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)

🪛 GitHub Check: Lint

[warning] 318-318:
Found non-literal argument to RegExp Constructor

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/serviceCost.ts` around lines 292 - 335, Harden parseUserData
against ReDoS from untrusted spec.validation patterns before invoking
re.test(value). Add a bounded, safe validation approach that rejects overly
complex or unsafe regex patterns, or evaluates them outside the main thread with
a timeout; preserve invalid-pattern warnings and existing mismatch errors for
accepted patterns.

Source: Linters/SAST tools

Comment thread src/tools/serviceTools.ts
environment: z
.string()
.describe('Env id — must be service-capable (findServiceEnvironments).'),
...serviceContainerSpecSchema,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f 'serviceSchemas.ts' --exec cat -n

Repository: oceanprotocol/on-mcp

Length of output: 16978


🏁 Script executed:

#!/bin/bash
set -euo pipefail
fd -t f 'serviceTools.ts' --exec sh -c 'wc -l "$1"; sed -n "680,810p" "$1"' sh {}

Repository: oceanprotocol/on-mcp

Length of output: 5514


Add a required-image guard to serviceStart. It shares serviceContainerSpecSchema.image, which is optional, so an omitted start image is cast to string as undefined and sent to the node. Add the check beside the image-mode/port validation before calling serviceEscrowGate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/serviceTools.ts` at line 696, Add a required-image validation guard
in serviceStart beside the existing image-mode/port checks, before
serviceEscrowGate is called. Reject requests when the start image is omitted or
undefined, rather than allowing the optional serviceContainerSpecSchema.image
value to be cast and forwarded to the node.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update MCP with inference tools

1 participant