@@ -17,10 +17,13 @@ report drift through `doctor --lint`. The final conformance signal is a clean
1717` doctor --lint ` run; policy contributes findings to that shared lint surface
1818instead of creating a separate health gate.
1919
20- Policy currently manages configured channels and governed tool declarations.
21- For example, IT or a workspace operator can record that Telegram is not an
22- approved channel provider, require governed tools to carry risk and sensitivity
23- metadata, then use ` doctor --lint ` as the shared conformance gate.
20+ Policy currently manages configured channels, MCP servers, model providers,
21+ network SSRF posture, and governed tool declarations. For example, IT or a
22+ workspace operator can record that Telegram is not an approved channel
23+ provider, restrict MCP servers and model refs to approved entries, require
24+ private-network fetch/browser access to remain disabled, require governed tools
25+ to carry risk and sensitivity metadata, then use ` doctor --lint ` as the shared
26+ conformance gate.
2427
2528Use policy when a workspace needs a durable statement such as "these channels
2629must not be enabled" or "governed tools must declare approval metadata" and a
@@ -41,7 +44,8 @@ arbitrary plugins. The plugin remains enabled if `policy.jsonc` is missing, so
4144doctor can report the missing artifact.
4245
4346Policy is authored, not generated from the user's current settings. A minimal
44- policy for channels and tool metadata looks like this:
47+ policy for channels, MCP servers, model providers, network posture, and tool
48+ metadata looks like this:
4549
4650``` jsonc
4751{
@@ -54,6 +58,23 @@ policy for channels and tool metadata looks like this:
5458 },
5559 ],
5660 },
61+ " mcp" : {
62+ " servers" : {
63+ " allow" : [" docs" ],
64+ " deny" : [" untrusted" ],
65+ },
66+ },
67+ " models" : {
68+ " providers" : {
69+ " allow" : [" openai" , " anthropic" ],
70+ " deny" : [" openrouter" ],
71+ },
72+ },
73+ " network" : {
74+ " privateNetwork" : {
75+ " allow" : false ,
76+ },
77+ },
5778 " tools" : {
5879 " requireMetadata" : [" risk" , " sensitivity" , " owner" ],
5980 },
@@ -62,8 +83,9 @@ policy for channels and tool metadata looks like this:
6283
6384The rules are the authority. A category block is only a namespace; checks run
6485when a concrete rule is present. OpenClaw reads current ` channels.* ` settings
65- and ` TOOLS.md ` declarations as evidence, then reports observed state that does
66- not conform.
86+ ` mcp.servers.* ` , ` models.providers.* ` , selected agent model refs, network SSRF
87+ settings, and ` TOOLS.md ` declarations as evidence, then reports observed state
88+ that does not conform.
6789
6890Run policy-only checks during authoring:
6991
@@ -167,6 +189,35 @@ Example JSON output:
167189 "enabled" : false
168190 }
169191 ],
192+ "mcpServers" : [
193+ {
194+ "id" : " docs" ,
195+ "transport" : " stdio" ,
196+ "source" : " oc://openclaw.config/mcp/servers/docs" ,
197+ "command" : " npx"
198+ }
199+ ],
200+ "modelProviders" : [
201+ {
202+ "id" : " openai" ,
203+ "source" : " oc://openclaw.config/models/providers/openai"
204+ }
205+ ],
206+ "modelRefs" : [
207+ {
208+ "ref" : " openai/gpt-5.5" ,
209+ "provider" : " openai" ,
210+ "model" : " gpt-5.5" ,
211+ "source" : " oc://openclaw.config/agents/defaults/model"
212+ }
213+ ],
214+ "network" : [
215+ {
216+ "id" : " browser-private-network" ,
217+ "source" : " oc://openclaw.config/browser/ssrfPolicy/dangerouslyAllowPrivateNetwork" ,
218+ "value" : false
219+ }
220+ ],
170221 "tools" : [
171222 {
172223 "id" : " deploy" ,
@@ -178,7 +229,7 @@ Example JSON output:
178229 }
179230 ]
180231 },
181- "checksRun" : 6 ,
232+ "checksRun" : 15 ,
182233 "checksSkipped" : 0 ,
183234 "findings" : []
184235}
@@ -226,18 +277,23 @@ choose a different interval.
226277
227278Policy currently verifies:
228279
229- | Check id | Finding |
230- | ---------------------------------------- | ------------------------------------------------------------------- |
231- | ` policy/policy-jsonc-missing ` | Policy is enabled but ` policy.jsonc ` is missing. |
232- | ` policy/policy-jsonc-invalid ` | Policy cannot be parsed or has malformed rules. |
233- | ` policy/policy-hash-mismatch ` | Policy does not match configured ` expectedHash ` . |
234- | ` policy/attestation-hash-mismatch ` | Current policy evidence no longer matches the accepted attestation. |
235- | ` policy/channels-denied-provider ` | An enabled channel matches a channel deny rule. |
236- | ` policy/tools-missing-owner ` | A governed tool declaration is missing owner metadata. |
237- | ` policy/tools-missing-risk-level ` | A governed tool declaration is missing risk metadata. |
238- | ` policy/tools-missing-sensitivity-token ` | A governed tool declaration is missing sensitivity metadata. |
239- | ` policy/tools-unknown-risk-level ` | A governed tool declaration uses an unknown risk value. |
240- | ` policy/tools-unknown-sensitivity-token ` | A governed tool declaration uses an unknown sensitivity value. |
280+ | Check id | Finding |
281+ | ---------------------------------------- | --------------------------------------------------------------------- |
282+ | ` policy/policy-jsonc-missing ` | Policy is enabled but ` policy.jsonc ` is missing. |
283+ | ` policy/policy-jsonc-invalid ` | Policy cannot be parsed or contains malformed rule entries. |
284+ | ` policy/policy-hash-mismatch ` | Policy does not match configured ` expectedHash ` . |
285+ | ` policy/attestation-hash-mismatch ` | Current policy evidence no longer matches the accepted attestation. |
286+ | ` policy/channels-denied-provider ` | An enabled channel matches a channel deny rule. |
287+ | ` policy/mcp-denied-server ` | A configured MCP server is denied by policy. |
288+ | ` policy/mcp-unapproved-server ` | A configured MCP server is outside the allowlist. |
289+ | ` policy/models-denied-provider ` | A configured model provider or model ref uses a denied provider. |
290+ | ` policy/models-unapproved-provider ` | A configured model provider or model ref is outside the allowlist. |
291+ | ` policy/network-private-access-enabled ` | A private-network SSRF escape hatch is enabled when policy denies it. |
292+ | ` policy/tools-missing-risk-level ` | A governed tool declaration is missing risk metadata. |
293+ | ` policy/tools-unknown-risk-level ` | A governed tool declaration uses an unknown risk value. |
294+ | ` policy/tools-missing-sensitivity-token ` | A governed tool declaration is missing sensitivity metadata. |
295+ | ` policy/tools-missing-owner ` | A governed tool declaration is missing owner metadata. |
296+ | ` policy/tools-unknown-sensitivity-token ` | A governed tool declaration uses an unknown sensitivity value. |
241297
242298Policy findings can include both ` target ` and ` requirement ` . ` target ` is the
243299observed workspace thing that does not conform. ` requirement ` is the authored
@@ -277,6 +333,51 @@ Example tool finding:
277333}
278334```
279335
336+ Example MCP finding:
337+
338+ ``` json
339+ {
340+ "checkId" : " policy/mcp-unapproved-server" ,
341+ "severity" : " error" ,
342+ "message" : " MCP server 'remote' is not in the policy allowlist." ,
343+ "source" : " policy" ,
344+ "path" : " openclaw config" ,
345+ "ocPath" : " oc://openclaw.config/mcp/servers/remote" ,
346+ "target" : " oc://openclaw.config/mcp/servers/remote" ,
347+ "requirement" : " oc://policy.jsonc/mcp/servers/allow"
348+ }
349+ ```
350+
351+ Example model-provider finding:
352+
353+ ``` json
354+ {
355+ "checkId" : " policy/models-unapproved-provider" ,
356+ "severity" : " error" ,
357+ "message" : " Model ref 'anthropic/claude-sonnet-4.7' uses unapproved provider 'anthropic'." ,
358+ "source" : " policy" ,
359+ "path" : " openclaw config" ,
360+ "ocPath" : " oc://openclaw.config/agents/defaults/model/fallbacks/#0" ,
361+ "target" : " oc://openclaw.config/agents/defaults/model/fallbacks/#0" ,
362+ "requirement" : " oc://policy.jsonc/models/providers/allow"
363+ }
364+ ```
365+
366+ Example network finding:
367+
368+ ``` json
369+ {
370+ "checkId" : " policy/network-private-access-enabled" ,
371+ "severity" : " error" ,
372+ "message" : " Network setting 'browser-private-network' allows private-network access." ,
373+ "source" : " policy" ,
374+ "path" : " openclaw config" ,
375+ "ocPath" : " oc://openclaw.config/browser/ssrfPolicy/dangerouslyAllowPrivateNetwork" ,
376+ "target" : " oc://openclaw.config/browser/ssrfPolicy/dangerouslyAllowPrivateNetwork" ,
377+ "requirement" : " oc://policy.jsonc/network/privateNetwork/allow"
378+ }
379+ ```
380+
280381## Repair
281382
282383` doctor --lint ` and ` policy check ` are read-only.
0 commit comments