oasr 0.4.2__tar.gz → 0.5.0__tar.gz
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.
- {oasr-0.4.2 → oasr-0.5.0}/CHANGELOG.md +41 -0
- {oasr-0.4.2 → oasr-0.5.0}/PKG-INFO +1 -1
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/CONFIG.md +57 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/EXEC.md +172 -0
- oasr-0.5.0/docs/config.example.toml +220 -0
- {oasr-0.4.2 → oasr-0.5.0}/pyproject.toml +1 -1
- {oasr-0.4.2 → oasr-0.5.0}/src/cli.py +1 -1
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/exec.py +46 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/config/__init__.py +10 -0
- oasr-0.5.0/src/config/defaults.py +40 -0
- oasr-0.5.0/src/config/schema.py +73 -0
- oasr-0.5.0/src/policy/__init__.py +50 -0
- oasr-0.5.0/src/policy/defaults.py +27 -0
- oasr-0.5.0/src/policy/enforcement.py +98 -0
- oasr-0.5.0/src/policy/profile.py +185 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_config.py +121 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_exec.py +44 -4
- oasr-0.5.0/tests/test_policy_enforcement.py +158 -0
- oasr-0.5.0/tests/test_policy_profile.py +184 -0
- {oasr-0.4.2 → oasr-0.5.0}/uv.lock +1 -1
- oasr-0.4.2/src/config/defaults.py +0 -16
- oasr-0.4.2/src/config/schema.py +0 -36
- {oasr-0.4.2 → oasr-0.5.0}/.gitignore +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/CONTRIBUTING.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/LICENSE +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/NOTICE +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/README.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.INDEX.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/adapter.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/add-glob.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/add-remote.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/add.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/find-add.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/find.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/help.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/info.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/list.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/rm-glob.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/rm.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/status.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/sync-update.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/sync.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/.images/use.png +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/QUICKSTART.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/.INDEX.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/ADAPTER.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/ADD.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/CLEAN.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/CLONE.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/DIFF.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/FIND.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/HELP.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/INFO.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/LIST.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/REGISTRY.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/RM.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/ROOT.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/STATUS.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/SYNC.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/UPDATE.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/USE.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/commands/VALIDATE.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/validation/.INDEX.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/validation/ERRORS.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/validation/INFO.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/validation/RULES.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/docs/validation/WARNINGS.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/install.ps1 +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/install.sh +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/llms.txt +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/scripts/README.md +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/scripts/fix.sh +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/scripts/lint.sh +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/scripts/test.sh +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/__init__.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/__main__.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/adapter.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/adapters/__init__.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/adapters/base.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/adapters/claude.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/adapters/codex.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/adapters/copilot.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/adapters/cursor.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/adapters/windsurf.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/agents/__init__.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/agents/base.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/agents/claude.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/agents/codex.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/agents/copilot.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/agents/opencode.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/agents/registry.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/__init__.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/adapter.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/add.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/clean.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/clone.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/config.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/diff.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/find.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/help.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/info.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/list.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/registry.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/rm.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/status.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/sync.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/update.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/use.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/commands/validate.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/discovery.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/manifest.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/registry.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/remote.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/skillcopy/__init__.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/skillcopy/local.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/skillcopy/remote.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/tracking.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/src/validate.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/conftest.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_adapters.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_agents.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_clone.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_config_command.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_copy.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_help.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_list.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_multi_skill.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_remote.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_tracking.py +0 -0
- {oasr-0.4.2 → oasr-0.5.0}/tests/test_use_glob.py +0 -0
|
@@ -4,6 +4,47 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.5.0] - 2026-02-02
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **🔒 Execution Policy System** — Host-level security boundaries for `oasr exec`
|
|
11
|
+
- Policy profiles define what agents can and cannot do
|
|
12
|
+
- Conservative safe defaults (fail closed)
|
|
13
|
+
- User-defined custom profiles in `config.toml`
|
|
14
|
+
- Pre-execution confirmation for risky operations
|
|
15
|
+
- Risk triggers: stdin, file prompts, non-safe profiles, network/env/shell access
|
|
16
|
+
- **New CLI flags for `oasr exec`**:
|
|
17
|
+
- `--profile <name>` — Choose execution policy profile
|
|
18
|
+
- `-y/--yes` — Skip confirmation prompt
|
|
19
|
+
- `--confirm` — Force confirmation even for safe operations
|
|
20
|
+
- **Configuration support for policies**:
|
|
21
|
+
- `[oasr]` section with `default_profile` setting
|
|
22
|
+
- `[profiles.<name>]` tables for custom profiles
|
|
23
|
+
- Policy field validation in config schema
|
|
24
|
+
- Built-in "safe" profile with conservative defaults
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- **Security model**: `oasr exec` now requires explicit confirmation for risky execution contexts
|
|
28
|
+
- **Config schema**: Extended to support execution policy profiles
|
|
29
|
+
|
|
30
|
+
### Security
|
|
31
|
+
- **Prompt injection mitigation**: Policy enforcement reduces impact of malicious skill instructions
|
|
32
|
+
- **Sensitive file protection**: Default deny list includes `~/.ssh`, `~/.aws`, `~/.gnupg`, `.env`, etc.
|
|
33
|
+
- **Execution boundaries**: Clear limits on filesystem access, network, environment variables, and shell commands
|
|
34
|
+
- **User awareness**: Policy summary shown before risky executions
|
|
35
|
+
- **Fail-closed design**: Missing/malformed config falls back to safe defaults
|
|
36
|
+
|
|
37
|
+
### Documentation
|
|
38
|
+
- **[EXEC.md](docs/commands/EXEC.md)**: Added comprehensive Security Model section
|
|
39
|
+
- **[CONFIG.md](docs/commands/CONFIG.md)**: Added Execution Policy Profiles documentation
|
|
40
|
+
- **Policy examples**: Multiple profile configurations for different use cases
|
|
41
|
+
|
|
42
|
+
### Technical
|
|
43
|
+
- **New module**: `src/policy/` subpackage with clean API (Profile, load, assess_risk, prompt_confirmation)
|
|
44
|
+
- **41 new tests**: 30 policy tests + 11 config profile tests
|
|
45
|
+
- **Test coverage**: 187 total tests passing (all existing tests still pass)
|
|
46
|
+
- **Backward compatible**: No breaking changes to existing commands
|
|
47
|
+
|
|
7
48
|
## [0.4.2] - 2026-02-01
|
|
8
49
|
|
|
9
50
|
### Added
|
|
@@ -241,6 +241,63 @@ Configure a different agent:
|
|
|
241
241
|
oasr config set agent copilot
|
|
242
242
|
```
|
|
243
243
|
|
|
244
|
+
## Execution Policy Profiles (v0.5.0+)
|
|
245
|
+
|
|
246
|
+
Policy profiles define security boundaries for `oasr exec`. They control what agents can do during skill execution.
|
|
247
|
+
|
|
248
|
+
### Default Profile
|
|
249
|
+
|
|
250
|
+
Set the default execution policy profile:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
oasr config set oasr.default_profile safe
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
This determines which profile is used unless overridden with `--profile`.
|
|
257
|
+
|
|
258
|
+
### Defining Custom Profiles
|
|
259
|
+
|
|
260
|
+
Add custom profiles to `~/.oasr/config.toml`:
|
|
261
|
+
|
|
262
|
+
```toml
|
|
263
|
+
[oasr]
|
|
264
|
+
default_profile = "safe"
|
|
265
|
+
|
|
266
|
+
# Conservative default
|
|
267
|
+
[profiles.safe]
|
|
268
|
+
fs_read_roots = ["./"]
|
|
269
|
+
fs_write_roots = ["./out", "./.oasr"]
|
|
270
|
+
deny_paths = ["~/.ssh", "~/.aws", "~/.gnupg", ".env"]
|
|
271
|
+
allowed_commands = ["rg", "fd", "jq", "cat"]
|
|
272
|
+
deny_shell = true
|
|
273
|
+
network = false
|
|
274
|
+
allow_env = false
|
|
275
|
+
|
|
276
|
+
# Development profile (more permissive)
|
|
277
|
+
[profiles.dev]
|
|
278
|
+
fs_read_roots = ["./", "~/projects"]
|
|
279
|
+
fs_write_roots = ["./", "~/projects/output"]
|
|
280
|
+
deny_paths = ["~/.ssh", "~/.aws"]
|
|
281
|
+
allowed_commands = ["bash", "curl", "git", "python"]
|
|
282
|
+
deny_shell = false
|
|
283
|
+
network = true
|
|
284
|
+
allow_env = true
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Profile Settings Reference
|
|
288
|
+
|
|
289
|
+
| Setting | Type | Description |
|
|
290
|
+
|---------|------|-------------|
|
|
291
|
+
| `fs_read_roots` | list[string] | Allowed filesystem read locations |
|
|
292
|
+
| `fs_write_roots` | list[string] | Allowed filesystem write locations |
|
|
293
|
+
| `deny_paths` | list[string] | Explicitly denied paths |
|
|
294
|
+
| `allowed_commands` | list[string] | Permitted shell commands |
|
|
295
|
+
| `deny_shell` | bool | Deny all shell execution |
|
|
296
|
+
| `network` | bool | Allow network access |
|
|
297
|
+
| `allow_env` | bool | Allow environment variable access |
|
|
298
|
+
|
|
299
|
+
See [`oasr exec` documentation](EXEC.md#security-model) for detailed security model explanation.
|
|
300
|
+
|
|
244
301
|
## Advanced Usage
|
|
245
302
|
|
|
246
303
|
### Direct Config File Editing
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Execute skills as CLI tools from anywhere on your system. Run skills with agent-driven execution without needing to clone them first.
|
|
4
4
|
|
|
5
|
+
> **🔒 Security Note**: As of v0.5.0, `oasr exec` includes host-level execution policy enforcement to protect against prompt injection and unsafe agent behavior. See [Security Model](#security-model) below.
|
|
6
|
+
|
|
5
7
|
## Usage
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -13,6 +15,9 @@ oasr exec <skill-name> [options]
|
|
|
13
15
|
- `-p, --prompt TEXT` — Inline prompt/instructions for the agent
|
|
14
16
|
- `-i, --instructions FILE` — Read prompt from a file
|
|
15
17
|
- `-a, --agent AGENT` — Override the default agent (codex, copilot, claude, opencode)
|
|
18
|
+
- `--profile PROFILE` — Use a specific execution policy profile (default: from config)
|
|
19
|
+
- `-y, --yes` — Skip confirmation prompt for risky operations
|
|
20
|
+
- `--confirm` — Force confirmation even for safe operations
|
|
16
21
|
|
|
17
22
|
## Features
|
|
18
23
|
|
|
@@ -311,6 +316,173 @@ This allows skills to:
|
|
|
311
316
|
- Tracking skill versions
|
|
312
317
|
- Working offline
|
|
313
318
|
|
|
319
|
+
## Security Model
|
|
320
|
+
|
|
321
|
+
### Overview
|
|
322
|
+
|
|
323
|
+
OASR enforces **host-level execution policies** to reduce the impact of prompt injection and unsafe agent behavior. Policies define what agents can and cannot do, protecting sensitive files and preventing unauthorized actions.
|
|
324
|
+
|
|
325
|
+
**Key Principles:**
|
|
326
|
+
- OASR does not judge skills
|
|
327
|
+
- OASR enforces user-defined host policy (execution ceilings)
|
|
328
|
+
- Skills and agents cannot override policy
|
|
329
|
+
- Policies stored in `~/.oasr/config.toml`
|
|
330
|
+
- Conservative defaults (fail closed)
|
|
331
|
+
|
|
332
|
+
### Policy Profiles
|
|
333
|
+
|
|
334
|
+
Execution policies are organized into profiles. Each profile defines:
|
|
335
|
+
|
|
336
|
+
| Setting | Description | Safe Default |
|
|
337
|
+
|---------|-------------|--------------|
|
|
338
|
+
| `fs_read_roots` | Allowed read locations | `["./"]` |
|
|
339
|
+
| `fs_write_roots` | Allowed write locations | `["./out", "./.oasr"]` |
|
|
340
|
+
| `deny_paths` | Explicitly denied paths | `["~/.ssh", "~/.aws", "~/.gnupg", ".env"]` |
|
|
341
|
+
| `allowed_commands` | Permitted shell commands | `["rg", "fd", "jq", "cat"]` |
|
|
342
|
+
| `deny_shell` | Block shell execution | `true` |
|
|
343
|
+
| `network` | Allow network access | `false` |
|
|
344
|
+
| `allow_env` | Allow environment access | `false` |
|
|
345
|
+
|
|
346
|
+
### Risk Triggers
|
|
347
|
+
|
|
348
|
+
OASR requires explicit confirmation when:
|
|
349
|
+
|
|
350
|
+
1. **Input from stdin** (non-interactive/piped)
|
|
351
|
+
2. **Prompt from file** (`-i/--instructions`)
|
|
352
|
+
3. **Non-safe profile** in use
|
|
353
|
+
4. **Environment access** enabled
|
|
354
|
+
5. **Network access** enabled
|
|
355
|
+
6. **Shell execution** allowed
|
|
356
|
+
7. **Force confirmation** flag (`--confirm`)
|
|
357
|
+
|
|
358
|
+
### Confirmation Flow
|
|
359
|
+
|
|
360
|
+
When risk triggers are detected:
|
|
361
|
+
|
|
362
|
+
```
|
|
363
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
364
|
+
EXECUTION POLICY REVIEW
|
|
365
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
366
|
+
Skill: csv-analyzer
|
|
367
|
+
Agent: codex
|
|
368
|
+
Profile: safe
|
|
369
|
+
Network: denied
|
|
370
|
+
Environment: denied
|
|
371
|
+
Shell: denied
|
|
372
|
+
Allowed commands: rg, fd, jq, cat
|
|
373
|
+
Read roots: ./
|
|
374
|
+
Write roots: ./out, ./.oasr
|
|
375
|
+
Deny paths: ~/.ssh, ~/.aws, ~/.gnupg, ~/.config, .env
|
|
376
|
+
|
|
377
|
+
⚠ This execution requires confirmation due to:
|
|
378
|
+
- Input from stdin (non-interactive)
|
|
379
|
+
|
|
380
|
+
Proceed? [y/N]
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
**Default answer is No** (safe).
|
|
384
|
+
|
|
385
|
+
### Configuration
|
|
386
|
+
|
|
387
|
+
Define profiles in `~/.oasr/config.toml`:
|
|
388
|
+
|
|
389
|
+
```toml
|
|
390
|
+
[oasr]
|
|
391
|
+
default_profile = "safe"
|
|
392
|
+
|
|
393
|
+
[profiles.safe]
|
|
394
|
+
fs_read_roots = ["./"]
|
|
395
|
+
fs_write_roots = ["./out", "./.oasr"]
|
|
396
|
+
deny_paths = ["~/.ssh", "~/.aws", "~/.gnupg", "~/.config", ".env"]
|
|
397
|
+
allowed_commands = ["rg", "fd", "jq", "cat"]
|
|
398
|
+
deny_shell = true
|
|
399
|
+
network = false
|
|
400
|
+
allow_env = false
|
|
401
|
+
|
|
402
|
+
[profiles.dev]
|
|
403
|
+
# More permissive for development
|
|
404
|
+
network = true
|
|
405
|
+
allow_env = true
|
|
406
|
+
deny_shell = false
|
|
407
|
+
allowed_commands = ["bash", "curl", "git", "python", "node"]
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Usage Examples
|
|
411
|
+
|
|
412
|
+
#### Safe Default (No Confirmation)
|
|
413
|
+
```bash
|
|
414
|
+
# Interactive prompt, safe profile
|
|
415
|
+
oasr exec csv-analyzer -p "Analyze data"
|
|
416
|
+
# No confirmation needed
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Stdin Triggers Confirmation
|
|
420
|
+
```bash
|
|
421
|
+
# Non-interactive input
|
|
422
|
+
echo "data" | oasr exec csv-analyzer
|
|
423
|
+
# Requires confirmation (unless --yes)
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
#### Skip Confirmation
|
|
427
|
+
```bash
|
|
428
|
+
# Use --yes to bypass (use carefully!)
|
|
429
|
+
echo "data" | oasr exec csv-analyzer --yes
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Use Different Profile
|
|
433
|
+
```bash
|
|
434
|
+
# Use 'dev' profile with more permissions
|
|
435
|
+
oasr exec api-tester -p "Run tests" --profile dev
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
#### Force Confirmation
|
|
439
|
+
```bash
|
|
440
|
+
# Force confirmation even if safe
|
|
441
|
+
oasr exec data-processor -p "Process" --confirm
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Best Practices
|
|
445
|
+
|
|
446
|
+
1. **Use safe defaults**: Start with the built-in safe profile
|
|
447
|
+
2. **Create custom profiles**: Define profiles for different trust levels
|
|
448
|
+
3. **Be explicit with --yes**: Only skip confirmation when you understand the risks
|
|
449
|
+
4. **Review policy before confirming**: Read the summary carefully
|
|
450
|
+
5. **Protect sensitive paths**: Always include `~/.ssh`, `~/.aws`, etc. in `deny_paths`
|
|
451
|
+
6. **Limit commands**: Only allow necessary commands in `allowed_commands`
|
|
452
|
+
7. **Use profiles per context**: Different profiles for dev/prod/testing
|
|
453
|
+
|
|
454
|
+
### What This Protects Against
|
|
455
|
+
|
|
456
|
+
✅ **Prompt injection attempts** that try to access sensitive files
|
|
457
|
+
✅ **Unsafe agent behavior** (network calls, shell execution)
|
|
458
|
+
✅ **Accidental exposure** of credentials or secrets
|
|
459
|
+
✅ **Unauthorized file access** outside allowed roots
|
|
460
|
+
✅ **Malicious skill instructions** that exceed policy
|
|
461
|
+
|
|
462
|
+
### What This Does NOT Do
|
|
463
|
+
|
|
464
|
+
❌ NLP-based prompt injection detection
|
|
465
|
+
❌ Prompt rewriting or sanitization
|
|
466
|
+
❌ Skill correctness validation
|
|
467
|
+
❌ Agent-specific permission models
|
|
468
|
+
❌ Plan-gated execution (staged for future)
|
|
469
|
+
|
|
470
|
+
### Limitations
|
|
471
|
+
|
|
472
|
+
- **Confirmation is on execution, not per-action**: Policy is checked before running, not during
|
|
473
|
+
- **Agent behavior is not monitored**: Once confirmed, agent runs with specified permissions
|
|
474
|
+
- **Trust required**: You must trust the agent CLI itself
|
|
475
|
+
- **File-level enforcement not yet implemented**: Path checks are advisory in v0.5.0
|
|
476
|
+
|
|
477
|
+
### Future Enhancements
|
|
478
|
+
|
|
479
|
+
Planned for future releases:
|
|
480
|
+
- Plan-gated execution (structured plan approval)
|
|
481
|
+
- Runtime file access enforcement
|
|
482
|
+
- Command allowlist enforcement via executor
|
|
483
|
+
- Network access controls
|
|
484
|
+
- Real-time policy violations monitoring
|
|
485
|
+
|
|
314
486
|
## Configuration
|
|
315
487
|
|
|
316
488
|
### Set Default Agent
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# OASR Configuration Example
|
|
2
|
+
# Location: ~/.oasr/config.toml
|
|
3
|
+
#
|
|
4
|
+
# This file demonstrates the configuration options for OASR v0.5.0+
|
|
5
|
+
# Copy relevant sections to your own config file.
|
|
6
|
+
|
|
7
|
+
# =============================================================================
|
|
8
|
+
# OASR Settings
|
|
9
|
+
# =============================================================================
|
|
10
|
+
[oasr]
|
|
11
|
+
# Default execution policy profile for 'oasr exec'
|
|
12
|
+
# Options: "safe" (default), or any custom profile name defined below
|
|
13
|
+
default_profile = "safe"
|
|
14
|
+
|
|
15
|
+
# =============================================================================
|
|
16
|
+
# Agent Configuration
|
|
17
|
+
# =============================================================================
|
|
18
|
+
[agent]
|
|
19
|
+
# Default agent for 'oasr exec' command
|
|
20
|
+
# Options: "codex", "copilot", "claude", "opencode", or null
|
|
21
|
+
default = "codex"
|
|
22
|
+
|
|
23
|
+
# =============================================================================
|
|
24
|
+
# Validation Settings
|
|
25
|
+
# =============================================================================
|
|
26
|
+
[validation]
|
|
27
|
+
# Maximum lines to show in skill reference output
|
|
28
|
+
reference_max_lines = 500
|
|
29
|
+
|
|
30
|
+
# Strict validation mode (fail on warnings)
|
|
31
|
+
strict = false
|
|
32
|
+
|
|
33
|
+
# =============================================================================
|
|
34
|
+
# Adapter Settings
|
|
35
|
+
# =============================================================================
|
|
36
|
+
[adapter]
|
|
37
|
+
# Default targets for 'oasr adapter' command
|
|
38
|
+
default_targets = ["cursor", "windsurf"]
|
|
39
|
+
|
|
40
|
+
# =============================================================================
|
|
41
|
+
# Execution Policy Profiles (v0.5.0+)
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# Policy profiles define security boundaries for 'oasr exec'.
|
|
44
|
+
# They control what agents can do during skill execution.
|
|
45
|
+
|
|
46
|
+
# -----------------------------------------------------------------------------
|
|
47
|
+
# Safe Profile (Built-in Default)
|
|
48
|
+
# -----------------------------------------------------------------------------
|
|
49
|
+
# Conservative default with minimal permissions
|
|
50
|
+
[profiles.safe]
|
|
51
|
+
fs_read_roots = ["./"] # Only read from current directory
|
|
52
|
+
fs_write_roots = ["./out", "./.oasr"] # Limited write locations
|
|
53
|
+
deny_paths = [ # Always deny these paths
|
|
54
|
+
"~/.ssh",
|
|
55
|
+
"~/.aws",
|
|
56
|
+
"~/.gnupg",
|
|
57
|
+
"~/.config",
|
|
58
|
+
".env",
|
|
59
|
+
"~/.bashrc",
|
|
60
|
+
"~/.zshrc",
|
|
61
|
+
"~/.profile",
|
|
62
|
+
]
|
|
63
|
+
allowed_commands = ["rg", "fd", "jq", "cat"] # Read-only tools
|
|
64
|
+
deny_shell = true # No shell execution
|
|
65
|
+
network = false # No network access
|
|
66
|
+
allow_env = false # No environment variables
|
|
67
|
+
|
|
68
|
+
# -----------------------------------------------------------------------------
|
|
69
|
+
# Development Profile
|
|
70
|
+
# -----------------------------------------------------------------------------
|
|
71
|
+
# More permissive for development work
|
|
72
|
+
[profiles.dev]
|
|
73
|
+
fs_read_roots = ["./", "~/projects"]
|
|
74
|
+
fs_write_roots = ["./", "~/projects/output", "./build", "./dist"]
|
|
75
|
+
deny_paths = [
|
|
76
|
+
"~/.ssh", # Still protect SSH keys
|
|
77
|
+
"~/.aws", # Still protect AWS credentials
|
|
78
|
+
"~/.gnupg", # Still protect GPG keys
|
|
79
|
+
]
|
|
80
|
+
allowed_commands = [
|
|
81
|
+
"bash", "sh", # Shell access
|
|
82
|
+
"curl", "wget", # Network tools
|
|
83
|
+
"git", # Version control
|
|
84
|
+
"python", "node", "go", # Programming languages
|
|
85
|
+
"npm", "pip", "cargo", # Package managers
|
|
86
|
+
"rg", "fd", "jq", # Search/parse tools
|
|
87
|
+
]
|
|
88
|
+
deny_shell = false # Allow shell commands
|
|
89
|
+
network = true # Allow network access
|
|
90
|
+
allow_env = true # Allow environment variables
|
|
91
|
+
|
|
92
|
+
# -----------------------------------------------------------------------------
|
|
93
|
+
# Testing Profile
|
|
94
|
+
# -----------------------------------------------------------------------------
|
|
95
|
+
# Isolated profile for running tests
|
|
96
|
+
[profiles.test]
|
|
97
|
+
fs_read_roots = ["./tests", "./fixtures", "./test-data"]
|
|
98
|
+
fs_write_roots = ["./test-output", "./coverage", "./.pytest_cache"]
|
|
99
|
+
deny_paths = [
|
|
100
|
+
"~/.ssh",
|
|
101
|
+
"~/.aws",
|
|
102
|
+
"~/", # Deny entire home directory
|
|
103
|
+
]
|
|
104
|
+
allowed_commands = [
|
|
105
|
+
"pytest", "coverage", # Python testing
|
|
106
|
+
"jest", "vitest", # JavaScript testing
|
|
107
|
+
"cargo", "go", # Other language test runners
|
|
108
|
+
"rg", "fd", # Search tools
|
|
109
|
+
]
|
|
110
|
+
deny_shell = true
|
|
111
|
+
network = false # Tests should not need network
|
|
112
|
+
allow_env = false
|
|
113
|
+
|
|
114
|
+
# -----------------------------------------------------------------------------
|
|
115
|
+
# CI/CD Profile
|
|
116
|
+
# -----------------------------------------------------------------------------
|
|
117
|
+
# Profile for continuous integration environments
|
|
118
|
+
[profiles.ci]
|
|
119
|
+
fs_read_roots = ["./", "/tmp/ci"]
|
|
120
|
+
fs_write_roots = ["./build", "./dist", "./coverage", "/tmp/ci"]
|
|
121
|
+
deny_paths = [
|
|
122
|
+
"~/.ssh", # Protect credentials
|
|
123
|
+
"~/.aws", # (CI should use injected secrets)
|
|
124
|
+
]
|
|
125
|
+
allowed_commands = [
|
|
126
|
+
"npm", "yarn", "pnpm",
|
|
127
|
+
"python", "pip",
|
|
128
|
+
"cargo", "go",
|
|
129
|
+
"git",
|
|
130
|
+
"docker", # Container builds
|
|
131
|
+
"kubectl", # Kubernetes
|
|
132
|
+
]
|
|
133
|
+
deny_shell = false
|
|
134
|
+
network = true # May need to fetch dependencies
|
|
135
|
+
allow_env = true # CI environment variables needed
|
|
136
|
+
|
|
137
|
+
# -----------------------------------------------------------------------------
|
|
138
|
+
# Data Analysis Profile
|
|
139
|
+
# -----------------------------------------------------------------------------
|
|
140
|
+
# Profile for data science and analysis tasks
|
|
141
|
+
[profiles.analysis]
|
|
142
|
+
fs_read_roots = ["./data", "./datasets", "./notebooks"]
|
|
143
|
+
fs_write_roots = ["./output", "./reports", "./plots"]
|
|
144
|
+
deny_paths = [
|
|
145
|
+
"~/.ssh",
|
|
146
|
+
"~/.aws",
|
|
147
|
+
"~/.gnupg",
|
|
148
|
+
]
|
|
149
|
+
allowed_commands = [
|
|
150
|
+
"python",
|
|
151
|
+
"jupyter",
|
|
152
|
+
"pandas",
|
|
153
|
+
"rg", "fd",
|
|
154
|
+
]
|
|
155
|
+
deny_shell = false # May need Python subprocess
|
|
156
|
+
network = false # Analysis should be offline
|
|
157
|
+
allow_env = false
|
|
158
|
+
|
|
159
|
+
# -----------------------------------------------------------------------------
|
|
160
|
+
# Production Profile
|
|
161
|
+
# -----------------------------------------------------------------------------
|
|
162
|
+
# Extremely restrictive profile for production use
|
|
163
|
+
[profiles.production]
|
|
164
|
+
fs_read_roots = ["./"]
|
|
165
|
+
fs_write_roots = ["./logs"] # Only write logs
|
|
166
|
+
deny_paths = [
|
|
167
|
+
"~/.ssh",
|
|
168
|
+
"~/.aws",
|
|
169
|
+
"~/.gnupg",
|
|
170
|
+
"~/.config",
|
|
171
|
+
".env",
|
|
172
|
+
"~/",
|
|
173
|
+
]
|
|
174
|
+
allowed_commands = ["rg", "cat"] # Minimal read-only
|
|
175
|
+
deny_shell = true
|
|
176
|
+
network = false
|
|
177
|
+
allow_env = false
|
|
178
|
+
|
|
179
|
+
# =============================================================================
|
|
180
|
+
# Usage Examples
|
|
181
|
+
# =============================================================================
|
|
182
|
+
#
|
|
183
|
+
# Use default profile:
|
|
184
|
+
# oasr exec my-skill -p "prompt"
|
|
185
|
+
#
|
|
186
|
+
# Use specific profile:
|
|
187
|
+
# oasr exec my-skill -p "prompt" --profile dev
|
|
188
|
+
#
|
|
189
|
+
# Skip confirmation (use carefully!):
|
|
190
|
+
# echo "data" | oasr exec my-skill --yes
|
|
191
|
+
#
|
|
192
|
+
# Force confirmation:
|
|
193
|
+
# oasr exec my-skill -p "prompt" --confirm
|
|
194
|
+
#
|
|
195
|
+
# Set default profile:
|
|
196
|
+
# oasr config set oasr.default_profile dev
|
|
197
|
+
#
|
|
198
|
+
# =============================================================================
|
|
199
|
+
# Security Notes
|
|
200
|
+
# =============================================================================
|
|
201
|
+
#
|
|
202
|
+
# 1. Always protect sensitive directories in deny_paths:
|
|
203
|
+
# - ~/.ssh (SSH keys)
|
|
204
|
+
# - ~/.aws (AWS credentials)
|
|
205
|
+
# - ~/.gnupg (GPG keys)
|
|
206
|
+
# - .env (environment secrets)
|
|
207
|
+
#
|
|
208
|
+
# 2. Use the most restrictive profile that works for your use case
|
|
209
|
+
#
|
|
210
|
+
# 3. Create custom profiles for different trust levels:
|
|
211
|
+
# - "safe" for untrusted skills
|
|
212
|
+
# - "dev" for development
|
|
213
|
+
# - "ci" for automation
|
|
214
|
+
# - Custom profiles for specific projects
|
|
215
|
+
#
|
|
216
|
+
# 4. Review policy summary before confirming risky operations
|
|
217
|
+
#
|
|
218
|
+
# 5. Use --yes flag sparingly and only when you understand the risks
|
|
219
|
+
#
|
|
220
|
+
# =============================================================================
|
|
@@ -13,7 +13,7 @@ from pathlib import Path
|
|
|
13
13
|
from commands import adapter, clean, clone, config, diff, exec, find, registry, sync, update, use, validate
|
|
14
14
|
from commands import help as help_cmd
|
|
15
15
|
|
|
16
|
-
__version__ = "0.
|
|
16
|
+
__version__ = "0.5.0"
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def main(argv: list[str] | None = None) -> int:
|
|
@@ -4,6 +4,7 @@ import argparse
|
|
|
4
4
|
import sys
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
import policy
|
|
7
8
|
from agents.registry import detect_available_agents, get_driver
|
|
8
9
|
from config import load_config
|
|
9
10
|
from registry import load_registry
|
|
@@ -42,6 +43,21 @@ def setup_parser(subparsers):
|
|
|
42
43
|
"--agent",
|
|
43
44
|
help="Override the default agent (codex, copilot, claude, opencode)",
|
|
44
45
|
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--profile",
|
|
48
|
+
help="Execution policy profile to use (default: from config)",
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"-y",
|
|
52
|
+
"--yes",
|
|
53
|
+
action="store_true",
|
|
54
|
+
help="Skip confirmation prompt for risky operations",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--confirm",
|
|
58
|
+
action="store_true",
|
|
59
|
+
help="Force confirmation prompt even for safe operations",
|
|
60
|
+
)
|
|
45
61
|
parser.set_defaults(func=run)
|
|
46
62
|
|
|
47
63
|
|
|
@@ -91,6 +107,36 @@ def run(args: argparse.Namespace) -> int:
|
|
|
91
107
|
# Error already printed by _get_agent_name
|
|
92
108
|
return 1
|
|
93
109
|
|
|
110
|
+
# === POLICY ENFORCEMENT ===
|
|
111
|
+
# Load configuration
|
|
112
|
+
config = load_config()
|
|
113
|
+
|
|
114
|
+
# Determine which profile to use (flag overrides config)
|
|
115
|
+
profile_name = args.profile if args.profile else config.get("oasr", {}).get("default_profile", "safe")
|
|
116
|
+
|
|
117
|
+
# Load the policy profile
|
|
118
|
+
profile = policy.load(config, profile_name)
|
|
119
|
+
|
|
120
|
+
# Detect execution context for risk assessment
|
|
121
|
+
stdin_used = not sys.stdin.isatty() and not args.prompt and not args.instructions
|
|
122
|
+
instructions_file_used = bool(args.instructions)
|
|
123
|
+
|
|
124
|
+
# Assess risk and determine if confirmation is needed
|
|
125
|
+
needs_confirm, reasons = policy.assess_risk(
|
|
126
|
+
profile=profile,
|
|
127
|
+
stdin_used=stdin_used,
|
|
128
|
+
instructions_file_used=instructions_file_used,
|
|
129
|
+
force_confirm=args.confirm,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Require confirmation unless --yes flag is present
|
|
133
|
+
if needs_confirm and not args.yes:
|
|
134
|
+
summary = policy.summarize(profile, skill_name, agent_name)
|
|
135
|
+
if not policy.prompt_confirmation(summary, reasons):
|
|
136
|
+
return 1 # User aborted
|
|
137
|
+
|
|
138
|
+
# === END POLICY ENFORCEMENT ===
|
|
139
|
+
|
|
94
140
|
# Get the agent driver
|
|
95
141
|
try:
|
|
96
142
|
driver = get_driver(agent_name)
|
|
@@ -56,6 +56,8 @@ def load_config(config_path: Path | None = None) -> dict[str, Any]:
|
|
|
56
56
|
"validation": DEFAULT_CONFIG["validation"].copy(),
|
|
57
57
|
"adapter": DEFAULT_CONFIG["adapter"].copy(),
|
|
58
58
|
"agent": DEFAULT_CONFIG["agent"].copy(),
|
|
59
|
+
"oasr": DEFAULT_CONFIG["oasr"].copy(),
|
|
60
|
+
"profiles": {k: v.copy() for k, v in DEFAULT_CONFIG["profiles"].items()},
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
if path.exists():
|
|
@@ -68,6 +70,12 @@ def load_config(config_path: Path | None = None) -> dict[str, Any]:
|
|
|
68
70
|
config["adapter"].update(loaded["adapter"])
|
|
69
71
|
if "agent" in loaded:
|
|
70
72
|
config["agent"].update(loaded["agent"])
|
|
73
|
+
if "oasr" in loaded:
|
|
74
|
+
config["oasr"].update(loaded["oasr"])
|
|
75
|
+
if "profiles" in loaded:
|
|
76
|
+
# Merge user profiles with defaults (user profiles take precedence)
|
|
77
|
+
for profile_name, profile_data in loaded["profiles"].items():
|
|
78
|
+
config["profiles"][profile_name] = profile_data
|
|
71
79
|
|
|
72
80
|
return config
|
|
73
81
|
|
|
@@ -106,4 +114,6 @@ def get_default_config() -> dict[str, Any]:
|
|
|
106
114
|
"validation": DEFAULT_CONFIG["validation"].copy(),
|
|
107
115
|
"adapter": DEFAULT_CONFIG["adapter"].copy(),
|
|
108
116
|
"agent": DEFAULT_CONFIG["agent"].copy(),
|
|
117
|
+
"oasr": DEFAULT_CONFIG["oasr"].copy(),
|
|
118
|
+
"profiles": {k: v.copy() for k, v in DEFAULT_CONFIG["profiles"].items()},
|
|
109
119
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Default configuration values."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
DEFAULT_CONFIG: dict[str, Any] = {
|
|
6
|
+
"validation": {
|
|
7
|
+
"reference_max_lines": 500,
|
|
8
|
+
"strict": False,
|
|
9
|
+
},
|
|
10
|
+
"adapter": {
|
|
11
|
+
"default_targets": ["cursor", "windsurf"],
|
|
12
|
+
},
|
|
13
|
+
"agent": {
|
|
14
|
+
"default": None,
|
|
15
|
+
},
|
|
16
|
+
"oasr": {
|
|
17
|
+
"default_profile": "safe",
|
|
18
|
+
},
|
|
19
|
+
"profiles": {
|
|
20
|
+
# Built-in safe profile (always available as fallback)
|
|
21
|
+
"safe": {
|
|
22
|
+
"fs_read_roots": ["./"],
|
|
23
|
+
"fs_write_roots": ["./out", "./.oasr"],
|
|
24
|
+
"deny_paths": [
|
|
25
|
+
"~/.ssh",
|
|
26
|
+
"~/.aws",
|
|
27
|
+
"~/.gnupg",
|
|
28
|
+
"~/.config",
|
|
29
|
+
".env",
|
|
30
|
+
"~/.bashrc",
|
|
31
|
+
"~/.zshrc",
|
|
32
|
+
"~/.profile",
|
|
33
|
+
],
|
|
34
|
+
"allowed_commands": ["rg", "fd", "jq", "cat"],
|
|
35
|
+
"deny_shell": True,
|
|
36
|
+
"network": False,
|
|
37
|
+
"allow_env": False,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
}
|