diff --git a/schema/runok.schema.json b/schema/runok.schema.json index e69de29..9275638 100644 --- a/schema/runok.schema.json +++ b/schema/runok.schema.json @@ -0,0 +1,322 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Config", + "description": "Top-level runok configuration.", + "type": "object", + "properties": { + "extends": { + "description": "List of configuration files to inherit from. Supports local paths and\nremote Git repositories (`github:/@`).", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "defaults": { + "description": "Default settings applied when no rule matches.", + "anyOf": [ + { + "$ref": "#/$defs/Defaults" + }, + { + "type": "null" + } + ] + }, + "rules": { + "description": "Ordered list of permission rules evaluated against each command.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/$defs/RuleEntry" + } + }, + "definitions": { + "description": "Reusable definitions for paths, sandbox presets, wrappers, and commands.", + "anyOf": [ + { + "$ref": "#/$defs/Definitions" + }, + { + "type": "null" + } + ] + } + }, + "$defs": { + "Defaults": { + "description": "Default settings applied when no rule matches a command.", + "type": "object", + "properties": { + "action": { + "description": "Default action when no rule matches: `allow`, `deny`, or `ask`.", + "anyOf": [ + { + "$ref": "#/$defs/ActionKind" + }, + { + "type": "null" + } + ] + }, + "sandbox": { + "description": "Default sandbox preset name to apply.", + "type": [ + "string", + "null" + ] + } + } + }, + "ActionKind": { + "description": "Permission action kind.", + "type": "string", + "enum": [ + "allow", + "ask", + "deny" + ] + }, + "RuleEntry": { + "description": "A permission rule entry. Exactly one of `deny`, `allow`, or `ask` must be set.", + "oneOf": [ + { + "type": "object", + "properties": { + "deny": { + "description": "Command pattern to deny. Matched commands are rejected.", + "type": "string" + }, + "when": { + "description": "CEL expression that must evaluate to true for this rule to apply.", + "type": [ + "string", + "null" + ] + }, + "message": { + "description": "Message shown when the rule matches (primarily for deny rules).", + "type": [ + "string", + "null" + ] + }, + "fix_suggestion": { + "description": "Suggested fix command shown when a deny rule matches.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "deny" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "allow": { + "description": "Command pattern to allow. Matched commands are permitted.", + "type": "string" + }, + "when": { + "description": "CEL expression that must evaluate to true for this rule to apply.", + "type": [ + "string", + "null" + ] + }, + "message": { + "description": "Message shown when the rule matches (primarily for deny rules).", + "type": [ + "string", + "null" + ] + }, + "fix_suggestion": { + "description": "Suggested fix command shown when a deny rule matches.", + "type": [ + "string", + "null" + ] + }, + "sandbox": { + "description": "Sandbox preset name to apply when this rule matches (not allowed for deny rules).", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "allow" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "ask": { + "description": "Command pattern to ask about. Matched commands prompt for confirmation.", + "type": "string" + }, + "when": { + "description": "CEL expression that must evaluate to true for this rule to apply.", + "type": [ + "string", + "null" + ] + }, + "message": { + "description": "Message shown when the rule matches (primarily for deny rules).", + "type": [ + "string", + "null" + ] + }, + "fix_suggestion": { + "description": "Suggested fix command shown when a deny rule matches.", + "type": [ + "string", + "null" + ] + }, + "sandbox": { + "description": "Sandbox preset name to apply when this rule matches (not allowed for deny rules).", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "ask" + ], + "additionalProperties": false + } + ] + }, + "Definitions": { + "description": "Reusable definitions for paths, sandbox presets, wrappers, and commands.", + "type": "object", + "properties": { + "paths": { + "description": "Named path lists referenced by `` in sandbox deny rules.", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "sandbox": { + "description": "Named sandbox presets that can be referenced by rules.", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/$defs/SandboxPreset" + } + }, + "wrappers": { + "description": "Wrapper command patterns for recursive evaluation (e.g., `sudo `).", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "commands": { + "description": "Additional command patterns to recognize.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + } + } + }, + "SandboxPreset": { + "description": "Sandbox preset defining filesystem and network restrictions.", + "type": "object", + "properties": { + "fs": { + "description": "Filesystem access policy.", + "anyOf": [ + { + "$ref": "#/$defs/FsPolicy" + }, + { + "type": "null" + } + ] + }, + "network": { + "description": "Network access policy.", + "anyOf": [ + { + "$ref": "#/$defs/NetworkPolicy" + }, + { + "type": "null" + } + ] + } + } + }, + "FsPolicy": { + "description": "Filesystem access policy within a sandbox preset.", + "type": "object", + "properties": { + "writable": { + "description": "Directories the sandboxed process is allowed to write to.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "deny": { + "description": "Paths the sandboxed process is denied access to. Supports `` references.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + } + } + }, + "NetworkPolicy": { + "description": "Network access policy within a sandbox preset.", + "type": "object", + "properties": { + "allow": { + "description": "Whether network access is allowed.", + "type": [ + "boolean", + "null" + ] + } + } + } + } +} diff --git a/src/cli/route.rs b/src/cli/route.rs index 7e0867b..477d5a7 100644 --- a/src/cli/route.rs +++ b/src/cli/route.rs @@ -35,7 +35,7 @@ pub fn route_check( let command = if args.command.len() == 1 { args.command[0].clone() } else { - shell_quote_join(&args.command) + shell_quote_join(&args.command)? }; return Ok(CheckRoute::Single(Box::new( CheckAdapter::from_command(command).with_output_format(output_format),