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.
Files changed (130) hide show
  1. {oasr-0.4.2 → oasr-0.5.0}/CHANGELOG.md +41 -0
  2. {oasr-0.4.2 → oasr-0.5.0}/PKG-INFO +1 -1
  3. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/CONFIG.md +57 -0
  4. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/EXEC.md +172 -0
  5. oasr-0.5.0/docs/config.example.toml +220 -0
  6. {oasr-0.4.2 → oasr-0.5.0}/pyproject.toml +1 -1
  7. {oasr-0.4.2 → oasr-0.5.0}/src/cli.py +1 -1
  8. {oasr-0.4.2 → oasr-0.5.0}/src/commands/exec.py +46 -0
  9. {oasr-0.4.2 → oasr-0.5.0}/src/config/__init__.py +10 -0
  10. oasr-0.5.0/src/config/defaults.py +40 -0
  11. oasr-0.5.0/src/config/schema.py +73 -0
  12. oasr-0.5.0/src/policy/__init__.py +50 -0
  13. oasr-0.5.0/src/policy/defaults.py +27 -0
  14. oasr-0.5.0/src/policy/enforcement.py +98 -0
  15. oasr-0.5.0/src/policy/profile.py +185 -0
  16. {oasr-0.4.2 → oasr-0.5.0}/tests/test_config.py +121 -0
  17. {oasr-0.4.2 → oasr-0.5.0}/tests/test_exec.py +44 -4
  18. oasr-0.5.0/tests/test_policy_enforcement.py +158 -0
  19. oasr-0.5.0/tests/test_policy_profile.py +184 -0
  20. {oasr-0.4.2 → oasr-0.5.0}/uv.lock +1 -1
  21. oasr-0.4.2/src/config/defaults.py +0 -16
  22. oasr-0.4.2/src/config/schema.py +0 -36
  23. {oasr-0.4.2 → oasr-0.5.0}/.gitignore +0 -0
  24. {oasr-0.4.2 → oasr-0.5.0}/CONTRIBUTING.md +0 -0
  25. {oasr-0.4.2 → oasr-0.5.0}/LICENSE +0 -0
  26. {oasr-0.4.2 → oasr-0.5.0}/NOTICE +0 -0
  27. {oasr-0.4.2 → oasr-0.5.0}/README.md +0 -0
  28. {oasr-0.4.2 → oasr-0.5.0}/docs/.INDEX.md +0 -0
  29. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/adapter.png +0 -0
  30. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/add-glob.png +0 -0
  31. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/add-remote.png +0 -0
  32. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/add.png +0 -0
  33. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/find-add.png +0 -0
  34. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/find.png +0 -0
  35. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/help.png +0 -0
  36. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/info.png +0 -0
  37. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/list.png +0 -0
  38. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/rm-glob.png +0 -0
  39. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/rm.png +0 -0
  40. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/status.png +0 -0
  41. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/sync-update.png +0 -0
  42. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/sync.png +0 -0
  43. {oasr-0.4.2 → oasr-0.5.0}/docs/.images/use.png +0 -0
  44. {oasr-0.4.2 → oasr-0.5.0}/docs/QUICKSTART.md +0 -0
  45. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/.INDEX.md +0 -0
  46. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/ADAPTER.md +0 -0
  47. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/ADD.md +0 -0
  48. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/CLEAN.md +0 -0
  49. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/CLONE.md +0 -0
  50. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/DIFF.md +0 -0
  51. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/FIND.md +0 -0
  52. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/HELP.md +0 -0
  53. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/INFO.md +0 -0
  54. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/LIST.md +0 -0
  55. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/REGISTRY.md +0 -0
  56. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/RM.md +0 -0
  57. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/ROOT.md +0 -0
  58. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/STATUS.md +0 -0
  59. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/SYNC.md +0 -0
  60. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/UPDATE.md +0 -0
  61. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/USE.md +0 -0
  62. {oasr-0.4.2 → oasr-0.5.0}/docs/commands/VALIDATE.md +0 -0
  63. {oasr-0.4.2 → oasr-0.5.0}/docs/validation/.INDEX.md +0 -0
  64. {oasr-0.4.2 → oasr-0.5.0}/docs/validation/ERRORS.md +0 -0
  65. {oasr-0.4.2 → oasr-0.5.0}/docs/validation/INFO.md +0 -0
  66. {oasr-0.4.2 → oasr-0.5.0}/docs/validation/RULES.md +0 -0
  67. {oasr-0.4.2 → oasr-0.5.0}/docs/validation/WARNINGS.md +0 -0
  68. {oasr-0.4.2 → oasr-0.5.0}/install.ps1 +0 -0
  69. {oasr-0.4.2 → oasr-0.5.0}/install.sh +0 -0
  70. {oasr-0.4.2 → oasr-0.5.0}/llms.txt +0 -0
  71. {oasr-0.4.2 → oasr-0.5.0}/scripts/README.md +0 -0
  72. {oasr-0.4.2 → oasr-0.5.0}/scripts/fix.sh +0 -0
  73. {oasr-0.4.2 → oasr-0.5.0}/scripts/lint.sh +0 -0
  74. {oasr-0.4.2 → oasr-0.5.0}/scripts/test.sh +0 -0
  75. {oasr-0.4.2 → oasr-0.5.0}/src/__init__.py +0 -0
  76. {oasr-0.4.2 → oasr-0.5.0}/src/__main__.py +0 -0
  77. {oasr-0.4.2 → oasr-0.5.0}/src/adapter.py +0 -0
  78. {oasr-0.4.2 → oasr-0.5.0}/src/adapters/__init__.py +0 -0
  79. {oasr-0.4.2 → oasr-0.5.0}/src/adapters/base.py +0 -0
  80. {oasr-0.4.2 → oasr-0.5.0}/src/adapters/claude.py +0 -0
  81. {oasr-0.4.2 → oasr-0.5.0}/src/adapters/codex.py +0 -0
  82. {oasr-0.4.2 → oasr-0.5.0}/src/adapters/copilot.py +0 -0
  83. {oasr-0.4.2 → oasr-0.5.0}/src/adapters/cursor.py +0 -0
  84. {oasr-0.4.2 → oasr-0.5.0}/src/adapters/windsurf.py +0 -0
  85. {oasr-0.4.2 → oasr-0.5.0}/src/agents/__init__.py +0 -0
  86. {oasr-0.4.2 → oasr-0.5.0}/src/agents/base.py +0 -0
  87. {oasr-0.4.2 → oasr-0.5.0}/src/agents/claude.py +0 -0
  88. {oasr-0.4.2 → oasr-0.5.0}/src/agents/codex.py +0 -0
  89. {oasr-0.4.2 → oasr-0.5.0}/src/agents/copilot.py +0 -0
  90. {oasr-0.4.2 → oasr-0.5.0}/src/agents/opencode.py +0 -0
  91. {oasr-0.4.2 → oasr-0.5.0}/src/agents/registry.py +0 -0
  92. {oasr-0.4.2 → oasr-0.5.0}/src/commands/__init__.py +0 -0
  93. {oasr-0.4.2 → oasr-0.5.0}/src/commands/adapter.py +0 -0
  94. {oasr-0.4.2 → oasr-0.5.0}/src/commands/add.py +0 -0
  95. {oasr-0.4.2 → oasr-0.5.0}/src/commands/clean.py +0 -0
  96. {oasr-0.4.2 → oasr-0.5.0}/src/commands/clone.py +0 -0
  97. {oasr-0.4.2 → oasr-0.5.0}/src/commands/config.py +0 -0
  98. {oasr-0.4.2 → oasr-0.5.0}/src/commands/diff.py +0 -0
  99. {oasr-0.4.2 → oasr-0.5.0}/src/commands/find.py +0 -0
  100. {oasr-0.4.2 → oasr-0.5.0}/src/commands/help.py +0 -0
  101. {oasr-0.4.2 → oasr-0.5.0}/src/commands/info.py +0 -0
  102. {oasr-0.4.2 → oasr-0.5.0}/src/commands/list.py +0 -0
  103. {oasr-0.4.2 → oasr-0.5.0}/src/commands/registry.py +0 -0
  104. {oasr-0.4.2 → oasr-0.5.0}/src/commands/rm.py +0 -0
  105. {oasr-0.4.2 → oasr-0.5.0}/src/commands/status.py +0 -0
  106. {oasr-0.4.2 → oasr-0.5.0}/src/commands/sync.py +0 -0
  107. {oasr-0.4.2 → oasr-0.5.0}/src/commands/update.py +0 -0
  108. {oasr-0.4.2 → oasr-0.5.0}/src/commands/use.py +0 -0
  109. {oasr-0.4.2 → oasr-0.5.0}/src/commands/validate.py +0 -0
  110. {oasr-0.4.2 → oasr-0.5.0}/src/discovery.py +0 -0
  111. {oasr-0.4.2 → oasr-0.5.0}/src/manifest.py +0 -0
  112. {oasr-0.4.2 → oasr-0.5.0}/src/registry.py +0 -0
  113. {oasr-0.4.2 → oasr-0.5.0}/src/remote.py +0 -0
  114. {oasr-0.4.2 → oasr-0.5.0}/src/skillcopy/__init__.py +0 -0
  115. {oasr-0.4.2 → oasr-0.5.0}/src/skillcopy/local.py +0 -0
  116. {oasr-0.4.2 → oasr-0.5.0}/src/skillcopy/remote.py +0 -0
  117. {oasr-0.4.2 → oasr-0.5.0}/src/tracking.py +0 -0
  118. {oasr-0.4.2 → oasr-0.5.0}/src/validate.py +0 -0
  119. {oasr-0.4.2 → oasr-0.5.0}/tests/conftest.py +0 -0
  120. {oasr-0.4.2 → oasr-0.5.0}/tests/test_adapters.py +0 -0
  121. {oasr-0.4.2 → oasr-0.5.0}/tests/test_agents.py +0 -0
  122. {oasr-0.4.2 → oasr-0.5.0}/tests/test_clone.py +0 -0
  123. {oasr-0.4.2 → oasr-0.5.0}/tests/test_config_command.py +0 -0
  124. {oasr-0.4.2 → oasr-0.5.0}/tests/test_copy.py +0 -0
  125. {oasr-0.4.2 → oasr-0.5.0}/tests/test_help.py +0 -0
  126. {oasr-0.4.2 → oasr-0.5.0}/tests/test_list.py +0 -0
  127. {oasr-0.4.2 → oasr-0.5.0}/tests/test_multi_skill.py +0 -0
  128. {oasr-0.4.2 → oasr-0.5.0}/tests/test_remote.py +0 -0
  129. {oasr-0.4.2 → oasr-0.5.0}/tests/test_tracking.py +0 -0
  130. {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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oasr
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: CLI for managing agent skills across IDE integrations
5
5
  Project-URL: Homepage, https://github.com/jgodau/asr
6
6
  Project-URL: Repository, https://github.com/jgodau/asr
@@ -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
+ # =============================================================================
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "oasr"
7
- version = "0.4.2"
7
+ version = "0.5.0"
8
8
  description = "CLI for managing agent skills across IDE integrations"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -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.4.2"
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
+ }