Zum Inhalt springen

Security Model

Dieser Inhalt ist noch nicht in deiner Sprache verfügbar.

FlowMCP enforces a layered security model that prevents schema files from accessing the network, filesystem, or process environment. All potentially dangerous operations are restricted to the trusted core runtime. Dependencies are injected through a factory function pattern, and external libraries are gated by an allowlist.

FlowMCP enforces a strict trust boundary between the core runtime and schema handlers:

flowchart LR
subgraph trusted ["TRUSTED ZONE - flowmcp-core"]
A[Static scan]
B[Validate main block]
C[Resolve shared lists]
D[Load libraries from allowlist]
E[Execute fetch]
F[Validate input/output]
end
subgraph restricted ["RESTRICTED ZONE - Schema Handlers"]
G["Receives: struct, payload"]
H["Receives: sharedLists, libraries"]
I["sharedLists is frozen"]
K["NO: import, require, eval, fs, process"]
L["CAN: use injected libs and sharedLists"]
end
A --> B --> C --> D --> E --> F
D --> H
E --> G

Trusted Zone (flowmcp-core):

  • Reads schema file as raw string and runs static security scan
  • Loads and validates the main export
  • Resolves shared list references and deep-freezes the data
  • Loads libraries from the allowlist
  • Executes HTTP fetch (handlers never fetch directly)
  • Validates input parameters and output schema

Restricted Zone (schema handlers):

  • Receives sharedLists and libraries through the factory function
  • Receives struct, payload, and response per-call
  • Transforms data (restructure, filter, compute)
  • Cannot access network, filesystem, process, or global scope

Before a schema is loaded, the raw file content is scanned for forbidden patterns. This happens before import() to prevent code execution.

PatternReason
import No imports — all dependencies are injected
require(No CommonJS imports
eval(Code injection
Function(Code injection
new FunctionCode injection
fs.Filesystem access
node:fsFilesystem access
fs/promisesFilesystem access
process.Process access
child_processShell execution
globalThis.Global scope access
global.Global scope access
__dirnamePath leaking
__filenamePath leaking
setTimeoutAsync side effects
setIntervalAsync side effects
1. Read file as raw string (before import)
2. Scan entire file for all forbidden patterns
3. If any pattern matches -> reject file with error message(s)
4. If clean -> proceed with dynamic import()

The entire file is scanned uniformly — no distinction between main and handler regions.

The runtime maintains an allowlist of approved npm packages. Only these packages can be declared in requiredLibraries and injected into handlers.

const DEFAULT_ALLOWLIST = [
'ethers',
'moment',
'indicatorts',
'@erc725/erc725.js',
'ccxt',
'axios'
]

Users can extend the allowlist in .flowmcp/config.json:

{
"security": {
"allowedLibraries": [ "custom-lib", "another-lib" ]
}
}

The effective allowlist is the union of default and user-extended lists.

flowchart TD
A[Read main.requiredLibraries] --> B{Each library on allowlist?}
B -->|Yes| C["Load via dynamic import()"]
B -->|No| D[Reject schema - SEC013]
C --> E[Package into libraries object]
E --> F["Inject into handlers()"]
  1. Read requiredLibraries — Extract the list of declared packages from the main block.
  2. Check allowlist — Every entry must appear in the default or user-extended allowlist.
  3. Load approved libraries — Each approved library is loaded via dynamic import().
  4. Inject into factory — The libraries object is passed to handlers( { sharedLists, libraries } ).

Shared list files have an even stricter scan:

AllowedForbidden
export const list = { meta: {...}, entries: [...] }Any function definition
String/number/boolean/null valuesasync, await, function, =>
Arrays and objectsAny schema forbidden patterns
Comments (//, /* */)Template literals with expressions

Shared lists are pure data. No logic, no transformations, no computed values.

Even after passing the static scan, handlers are constrained:

  1. No fetch access — the runtime executes fetch and passes the response to postRequest
  2. No side effects — receive data, return data. No logging, no file writes
  3. sharedLists is read-only — deep-frozen via Object.freeze(). Mutations throw TypeError
  4. Only allowlisted packages — non-injected packages are not in scope
  5. Return value required — must return the expected shape
// preRequest — modify the request before fetch
preRequest: async ( { struct, payload } ) => {
// struct: the request structure (url, headers, body)
// payload: resolved route parameters
// sharedLists + libraries: available via factory closure
return { struct, payload }
}
// postRequest — transform the response after fetch
postRequest: async ( { response, struct, payload } ) => {
// response: parsed JSON from the API
// sharedLists + libraries: available via factory closure
return { response }
}
flowchart LR
A[".env: ETHERSCAN_API_KEY=abc123"] --> B[Core Runtime]
B --> C["URL: ?apikey=abc123"]
B -.->|NOT passed| D[Handler Factory]
B -.->|NOT passed| F[Handler Per-Call]
  • requiredServerParams values are injected into URL/headers by the runtime
  • The handlers() factory receives sharedLists and libraries only
  • Per-call handlers receive struct, payload, and response only
  • Key values are never logged
1. Schema declares requiredServerParams: [ 'ETHERSCAN_API_KEY' ]
2. Runtime reads ETHERSCAN_API_KEY from .env
3. Parameter template: '{{SERVER_PARAM:ETHERSCAN_API_KEY}}'
4. Runtime substitutes into URL: '?apikey=abc123'
5. Handler receives response — never sees the key value
ThreatMitigation
Schema imports a moduleStatic scan blocks import/require
Schema requests unapproved libraryBlocked by allowlist (SEC013)
Schema reads filesystemStatic scan blocks fs, node:fs
Schema executes shell commandsStatic scan blocks child_process
Schema accesses environmentStatic scan blocks process.
Schema exfiltrates data via fetchHandlers cannot call fetch()
Schema modifies global stateStatic scan blocks globalThis/global.
Handler mutates shared list datasharedLists is deep-frozen
Shared list contains executable codeStricter scan blocks all functions/arrows
Schema leaks API keysKeys never passed to factory or handlers
Schema uses evalStatic scan blocks eval(, Function(
CodeDescription
SEC001Forbidden import statement found
SEC002Forbidden require() call found
SEC003Forbidden eval() call found
SEC004Forbidden Function() constructor found
SEC005Forbidden filesystem access
SEC006Forbidden process. access
SEC007Forbidden child_process access
SEC008Forbidden global scope access
SEC009Forbidden path variable
SEC010Forbidden new Function
SEC011Forbidden timer (setTimeout/setInterval)
SEC013Unapproved library in requiredLibraries

SEC100-SEC199 — Runtime Constraint Violations

Section titled “SEC100-SEC199 — Runtime Constraint Violations”
CodeDescription
SEC100Handler attempted to call fetch()
SEC101Handler returned invalid shape
SEC102Handler attempted to mutate frozen sharedLists
SEC103Library loading failed for approved package
SEC104Factory function handlers() threw during initialization

SEC200-SEC299 — Shared List Scan Failures

Section titled “SEC200-SEC299 — Shared List Scan Failures”
CodeDescription
SEC200Function definition found in shared list
SEC201Arrow function found in shared list
SEC202Async/await keyword found in shared list
SEC203Template literal with expression found
SEC204Forbidden pattern found in shared list

All violations in a single file are reported together — the scan does not stop at the first match.