agentsecure 0.1.3__tar.gz → 0.1.4__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 (115) hide show
  1. {agentsecure-0.1.3/agentsecure.egg-info → agentsecure-0.1.4}/PKG-INFO +16 -3
  2. {agentsecure-0.1.3 → agentsecure-0.1.4}/README.md +15 -2
  3. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/__init__.py +1 -1
  4. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/main.py +7 -0
  5. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/policy.py +8 -1
  6. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/project.py +9 -0
  7. agentsecure-0.1.4/agentsecure/core/agentsecure_md.py +188 -0
  8. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/product.py +16 -0
  9. {agentsecure-0.1.3 → agentsecure-0.1.4/agentsecure.egg-info}/PKG-INFO +16 -3
  10. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure.egg-info/SOURCES.txt +2 -0
  11. {agentsecure-0.1.3 → agentsecure-0.1.4}/setup.cfg +1 -1
  12. agentsecure-0.1.4/tests/test_agentsecure_md.py +66 -0
  13. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_product.py +2 -0
  14. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_version.py +7 -0
  15. {agentsecure-0.1.3 → agentsecure-0.1.4}/LICENSE +0 -0
  16. {agentsecure-0.1.3 → agentsecure-0.1.4}/NOTICE +0 -0
  17. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/__main__.py +0 -0
  18. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/api/__init__.py +0 -0
  19. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/api/server.py +0 -0
  20. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/api/services.py +0 -0
  21. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/__init__.py +0 -0
  22. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/common.py +0 -0
  23. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/demo.py +0 -0
  24. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/proxy.py +0 -0
  25. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/receipts.py +0 -0
  26. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/secrets.py +0 -0
  27. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/settings.py +0 -0
  28. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/client/__init__.py +0 -0
  29. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/client/wrappers.py +0 -0
  30. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cloud.py +0 -0
  31. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/config/__init__.py +0 -0
  32. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/config/default_agentsecure.json +0 -0
  33. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/__init__.py +0 -0
  34. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/capabilities.py +0 -0
  35. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/command_metadata.py +0 -0
  36. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/config.py +0 -0
  37. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/config_profiles.py +0 -0
  38. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/container.py +0 -0
  39. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/key_service.py +0 -0
  40. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/models.py +0 -0
  41. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/policy_mutation.py +0 -0
  42. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/policy_ports.py +0 -0
  43. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/policy_response.py +0 -0
  44. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/policy_validation.py +0 -0
  45. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/provider_proxy.py +0 -0
  46. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/time.py +0 -0
  47. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/crypto/__init__.py +0 -0
  48. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/crypto/cipher.py +0 -0
  49. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/crypto/key_provider.py +0 -0
  50. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/__init__.py +0 -0
  51. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/commands.py +0 -0
  52. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/policies.py +0 -0
  53. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/sessions.py +0 -0
  54. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/supervisor.py +0 -0
  55. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/__init__.py +0 -0
  56. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/dotenv_scanner.py +0 -0
  57. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/env_scanner.py +0 -0
  58. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/patterns.py +0 -0
  59. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/scanner.py +0 -0
  60. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/suggestions.py +0 -0
  61. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/gateway/__init__.py +0 -0
  62. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/gateway/proxy.py +0 -0
  63. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/__init__.py +0 -0
  64. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/command.py +0 -0
  65. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/network.py +0 -0
  66. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/sanitizer.py +0 -0
  67. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/wrappers.py +0 -0
  68. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/__init__.py +0 -0
  69. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/audit.py +0 -0
  70. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/encrypted_secret_store.py +0 -0
  71. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/grant_store.py +0 -0
  72. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/local_secret_store.py +0 -0
  73. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/policy.py +0 -0
  74. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/secret_store_factory.py +0 -0
  75. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/secrets.py +0 -0
  76. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/__init__.py +0 -0
  77. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/audit.py +0 -0
  78. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/grants.py +0 -0
  79. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/key_store.py +0 -0
  80. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/policy.py +0 -0
  81. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/secrets.py +0 -0
  82. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/__init__.py +0 -0
  83. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/apply.py +0 -0
  84. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/diff.py +0 -0
  85. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/materializer.py +0 -0
  86. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/rewriter.py +0 -0
  87. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/strategies.py +0 -0
  88. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure.egg-info/dependency_links.txt +0 -0
  89. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure.egg-info/entry_points.txt +0 -0
  90. {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure.egg-info/top_level.txt +0 -0
  91. {agentsecure-0.1.3 → agentsecure-0.1.4}/pyproject.toml +0 -0
  92. {agentsecure-0.1.3 → agentsecure-0.1.4}/setup.py +0 -0
  93. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_audit.py +0 -0
  94. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_cli.py +0 -0
  95. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_cli_demo.py +0 -0
  96. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_cli_policy.py +0 -0
  97. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_cli_proxy.py +0 -0
  98. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_config_validation.py +0 -0
  99. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_diff.py +0 -0
  100. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_discovery.py +0 -0
  101. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_encrypted_secret_store.py +0 -0
  102. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_env_policy.py +0 -0
  103. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_env_policy_contract.py +0 -0
  104. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_gateway_credentials.py +0 -0
  105. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_guard.py +0 -0
  106. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_guard_network.py +0 -0
  107. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_key_service.py +0 -0
  108. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_policy.py +0 -0
  109. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_policy_mutation.py +0 -0
  110. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_provider_proxy.py +0 -0
  111. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_secret_scan.py +0 -0
  112. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_secrets.py +0 -0
  113. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_workspace.py +0 -0
  114. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_workspace_apply.py +0 -0
  115. {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_wrappers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentsecure
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Local-first security runtime for AI coding agents
5
5
  Home-page: https://github.com/ShellFrameAI/agentsecure-community
6
6
  Author: ShellFrame AI
@@ -76,12 +76,18 @@ Why:
76
76
 
77
77
  ## Quickstart In A Project
78
78
 
79
- Create a local config:
79
+ Create a local config and repo guidance file:
80
80
 
81
81
  ```bash
82
82
  agentsecure init
83
83
  ```
84
84
 
85
+ This creates `agentsecure.json`, local private state under `.agentsecure/`, and `AGENTSECURE.md`. Review the Markdown file before running agents:
86
+
87
+ ```bash
88
+ agentsecure policy validate
89
+ ```
90
+
85
91
  Create a fake `.env` for testing:
86
92
 
87
93
  ```bash
@@ -166,7 +172,7 @@ Command-guard mode is a usability guard, not a hard sandbox. A determined proces
166
172
 
167
173
  ## Example Policy
168
174
 
169
- See [examples/agentsecure.community.json](examples/agentsecure.community.json) and [examples/.env.example](examples/.env.example).
175
+ See [examples/agentsecure.community.json](examples/agentsecure.community.json), [examples/AGENTSECURE.md](examples/AGENTSECURE.md), and [examples/.env.example](examples/.env.example).
170
176
 
171
177
  Minimal policy shape:
172
178
 
@@ -195,6 +201,7 @@ Minimal policy shape:
195
201
 
196
202
  ```bash
197
203
  agentsecure init
204
+ agentsecure policy validate
198
205
  agentsecure status
199
206
  agentsecure doctor
200
207
  agentsecure discover
@@ -269,6 +276,12 @@ python3 scripts/secret_scan.py .
269
276
 
270
277
  CI runs tests across supported Python versions and runs the local secret scan.
271
278
 
279
+ ## AGENTSECURE.md
280
+
281
+ `AGENTSECURE.md` is a small repo-level policy guidance file for humans and AI coding agents. In the community release, AgentSecure creates it and validates that it does not contain raw secrets or unsupported raw-secret passthrough modes.
282
+
283
+ Supported community secret modes in the Markdown guidance are `virtualize` and `deny`. Do not use `allow` or `allow_real` for secrets. The Markdown file is guidance plus local validation; it is not a full sandbox by itself.
284
+
272
285
  ## Public Release Boundary
273
286
 
274
287
  This community release does not include hosted backend integration, enterprise policy sync, billing/licensing, production secrets, internal endpoints, or sensitive commercial heuristics. See [OPEN_SOURCE_PLAN.md](OPEN_SOURCE_PLAN.md) and [OPEN_SOURCE_READINESS_REPORT.md](OPEN_SOURCE_READINESS_REPORT.md) for the public/private boundary.
@@ -50,12 +50,18 @@ Why:
50
50
 
51
51
  ## Quickstart In A Project
52
52
 
53
- Create a local config:
53
+ Create a local config and repo guidance file:
54
54
 
55
55
  ```bash
56
56
  agentsecure init
57
57
  ```
58
58
 
59
+ This creates `agentsecure.json`, local private state under `.agentsecure/`, and `AGENTSECURE.md`. Review the Markdown file before running agents:
60
+
61
+ ```bash
62
+ agentsecure policy validate
63
+ ```
64
+
59
65
  Create a fake `.env` for testing:
60
66
 
61
67
  ```bash
@@ -140,7 +146,7 @@ Command-guard mode is a usability guard, not a hard sandbox. A determined proces
140
146
 
141
147
  ## Example Policy
142
148
 
143
- See [examples/agentsecure.community.json](examples/agentsecure.community.json) and [examples/.env.example](examples/.env.example).
149
+ See [examples/agentsecure.community.json](examples/agentsecure.community.json), [examples/AGENTSECURE.md](examples/AGENTSECURE.md), and [examples/.env.example](examples/.env.example).
144
150
 
145
151
  Minimal policy shape:
146
152
 
@@ -169,6 +175,7 @@ Minimal policy shape:
169
175
 
170
176
  ```bash
171
177
  agentsecure init
178
+ agentsecure policy validate
172
179
  agentsecure status
173
180
  agentsecure doctor
174
181
  agentsecure discover
@@ -243,6 +250,12 @@ python3 scripts/secret_scan.py .
243
250
 
244
251
  CI runs tests across supported Python versions and runs the local secret scan.
245
252
 
253
+ ## AGENTSECURE.md
254
+
255
+ `AGENTSECURE.md` is a small repo-level policy guidance file for humans and AI coding agents. In the community release, AgentSecure creates it and validates that it does not contain raw secrets or unsupported raw-secret passthrough modes.
256
+
257
+ Supported community secret modes in the Markdown guidance are `virtualize` and `deny`. Do not use `allow` or `allow_real` for secrets. The Markdown file is guidance plus local validation; it is not a full sandbox by itself.
258
+
246
259
  ## Public Release Boundary
247
260
 
248
261
  This community release does not include hosted backend integration, enterprise policy sync, billing/licensing, production secrets, internal endpoints, or sensitive commercial heuristics. See [OPEN_SOURCE_PLAN.md](OPEN_SOURCE_PLAN.md) and [OPEN_SOURCE_READINESS_REPORT.md](OPEN_SOURCE_READINESS_REPORT.md) for the public/private boundary.
@@ -1,3 +1,3 @@
1
1
  """AgentSecure local-first security runtime."""
2
2
 
3
- __version__ = "0.1.3"
3
+ __version__ = "0.1.4"
@@ -14,6 +14,7 @@ import time
14
14
  from typing import Any, Dict, List, Optional
15
15
  from urllib.request import Request, urlopen
16
16
 
17
+ from agentsecure import __version__
17
18
  from agentsecure.cloud import CloudError, CloudRuntimeService
18
19
  from agentsecure.api.server import LocalApiServer
19
20
  from agentsecure.api.services import ApiServices
@@ -49,6 +50,7 @@ from agentsecure.cli.settings import (
49
50
  handle_setup,
50
51
  )
51
52
  from agentsecure.client.wrappers import AgentWrapperInstaller, SUPPORTED_AGENTS
53
+ from agentsecure.core.agentsecure_md import AGENTSECURE_MD, agentsecure_md_status
52
54
  from agentsecure.core.command_metadata import safe_command_metadata
53
55
  from agentsecure.core.config import ConfigError, JsonConfigLoader, JsonConfigWriter
54
56
  from agentsecure.core.config_profiles import (
@@ -145,6 +147,7 @@ def main(argv: Optional[List[str]] = None) -> int:
145
147
 
146
148
  def build_parser() -> argparse.ArgumentParser:
147
149
  parser = argparse.ArgumentParser(prog="agentsecure")
150
+ parser.add_argument("--version", action="version", version="agentsecure %s" % __version__)
148
151
  parser.add_argument(
149
152
  "--config",
150
153
  default="agentsecure.json",
@@ -264,6 +267,10 @@ def build_parser() -> argparse.ArgumentParser:
264
267
 
265
268
 
266
269
  def run_agent(args: argparse.Namespace) -> int:
270
+ policy_doc = agentsecure_md_status(AGENTSECURE_MD)
271
+ if policy_doc.get("exists"):
272
+ state = "valid" if policy_doc.get("ok") else "needs review"
273
+ print("AgentSecure policy guidance: %s (%s)" % (AGENTSECURE_MD, state), flush=True)
267
274
  cloud = CloudRuntimeService() if _cloud_features_enabled() else None
268
275
  if cloud:
269
276
  _apply_cloud_runtime_defaults(args, cloud)
@@ -2,6 +2,7 @@ import argparse
2
2
  import json
3
3
  import sys
4
4
 
5
+ from agentsecure.core.agentsecure_md import AGENTSECURE_MD, validate_agentsecure_md
5
6
  from agentsecure.core.config import ConfigError
6
7
  from agentsecure.core.policy_mutation import LocalPolicyMutationService
7
8
 
@@ -14,11 +15,17 @@ def add_policy_subparser(subparsers) -> None:
14
15
  policy_preview_parser.add_argument("--json-file", help="Read mutation JSON from this file instead of stdin")
15
16
  policy_apply_parser = policy_subparsers.add_parser("apply-local", help="Apply local policy changes from JSON")
16
17
  policy_apply_parser.add_argument("--json-file", help="Read mutation JSON from this file instead of stdin")
18
+ validate_parser = policy_subparsers.add_parser("validate", help="Validate AGENTSECURE.md policy guidance")
19
+ validate_parser.add_argument("--file", default=AGENTSECURE_MD, help="AGENTSECURE.md path")
17
20
 
18
21
 
19
22
  def handle_policy(args: argparse.Namespace) -> int:
20
- service = LocalPolicyMutationService(args.config)
21
23
  try:
24
+ if args.policy_command == "validate":
25
+ payload = validate_agentsecure_md(args.file)
26
+ print(json.dumps(payload, indent=2, sort_keys=True))
27
+ return 0 if payload["ok"] else 1
28
+ service = LocalPolicyMutationService(args.config)
22
29
  if args.policy_command == "review":
23
30
  payload = service.review()
24
31
  elif args.policy_command == "preview":
@@ -4,6 +4,7 @@ import os
4
4
  import shutil
5
5
 
6
6
  from agentsecure.cli.common import scanner
7
+ from agentsecure.core.agentsecure_md import AGENTSECURE_MD
7
8
  from agentsecure.core.product import ProductService
8
9
  from agentsecure.workspace.materializer import make_tree_writable
9
10
 
@@ -32,6 +33,14 @@ def show_status(args: argparse.Namespace) -> int:
32
33
  return 0
33
34
  print("AgentSecure status")
34
35
  print("Config: %s (%s)" % (result["config_path"], "found" if result["config_exists"] else "missing"))
36
+ agentsecure_md = result.get("agentsecure_md", {})
37
+ print(
38
+ "AGENTSECURE.md: %s (%s)"
39
+ % (
40
+ agentsecure_md.get("path", AGENTSECURE_MD),
41
+ "valid" if agentsecure_md.get("exists") and agentsecure_md.get("ok") else "missing" if not agentsecure_md.get("exists") else "needs review",
42
+ )
43
+ )
35
44
  print("Configured secrets: %s" % result["configured_secrets"])
36
45
  print("Discovered secrets: %s" % result["discovered_secrets"])
37
46
  grants = result["grants"]
@@ -0,0 +1,188 @@
1
+ import os
2
+ import re
3
+ from typing import Any, Dict, List
4
+
5
+
6
+ AGENTSECURE_MD = "AGENTSECURE.md"
7
+ AGENTSECURE_MD_TEMPLATE_VERSION = "0.1.4"
8
+
9
+ AGENTSECURE_MD_TEMPLATE = """# AGENTSECURE.md
10
+
11
+ ## Start
12
+
13
+ Run coding agents through AgentSecure:
14
+
15
+ ```bash
16
+ agentsecure run --protect-all -- claude
17
+ ```
18
+
19
+ ## Secrets
20
+
21
+ Do not paste real secrets, raw `.env` files, private keys, tokens, prompts, or request bodies into this file.
22
+
23
+ ```yaml
24
+ DATABASE_URL_DEV:
25
+ mode: virtualize
26
+ note: use a local/dev-only value approved by the human
27
+
28
+ DATABASE_URL_PROD:
29
+ mode: deny
30
+
31
+ OPENAI_API_KEY:
32
+ mode: virtualize
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ Allowed:
38
+ - npm test
39
+ - npm run build
40
+
41
+ Blocked:
42
+ - printenv
43
+ - env
44
+
45
+ Require approval:
46
+ - production deploys
47
+ - database migrations
48
+
49
+ ## Network
50
+
51
+ Allowed:
52
+ - localhost
53
+ - test-dev.example.internal
54
+
55
+ Blocked:
56
+ - prod.example.internal
57
+ - unknown private IPs
58
+ - credential-bearing requests to unapproved domains
59
+
60
+ ## When Policy Denies Access
61
+
62
+ Do not retry the same credential.
63
+ Use the suggested safe alternative when one exists.
64
+ Ask the human before requesting broader access.
65
+
66
+ ## Local-First Trust
67
+
68
+ Real secrets stay on the developer machine. Community AgentSecure validates this guidance locally. Team profile sync, assignment, and audit visibility are commercial ShellFrame Console features.
69
+ """
70
+
71
+
72
+ SECRET_TOKEN_PATTERNS = [
73
+ re.compile(r"sk_(?:live|test)_[A-Za-z0-9]{10,}", re.IGNORECASE),
74
+ re.compile(r"sk-(?:live|test|proj)-[A-Za-z0-9_-]{10,}", re.IGNORECASE),
75
+ re.compile(r"rk_live_[A-Za-z0-9]{10,}", re.IGNORECASE),
76
+ re.compile(r"github_pat_[A-Za-z0-9_]{20,}", re.IGNORECASE),
77
+ re.compile(r"gh[pousr]_[A-Za-z0-9_]{20,}", re.IGNORECASE),
78
+ re.compile(r"xox[baprs]-[A-Za-z0-9-]{16,}", re.IGNORECASE),
79
+ re.compile(r"AKIA[0-9A-Z]{16}"),
80
+ re.compile(r"AIza[0-9A-Za-z_-]{20,}"),
81
+ re.compile(r"eyJ[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}"),
82
+ ]
83
+
84
+ SECRET_ASSIGNMENT_RE = re.compile(
85
+ r"^\s*([A-Z0-9_]*(?:SECRET|TOKEN|PASSWORD|PASS|API_KEY|PRIVATE_KEY|DATABASE_URL|DB_URL|CREDENTIAL)[A-Z0-9_]*)\s*=\s*(.+?)\s*$",
86
+ re.IGNORECASE,
87
+ )
88
+
89
+ PRIVATE_KEY_RE = re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----")
90
+ FORBIDDEN_MODE_RE = re.compile(r"^\s*mode:\s*(allow|allow[_-]?real)\s*$", re.IGNORECASE)
91
+ ALLOW_PRODUCTION_SECRET_RE = re.compile(
92
+ r"\ballow\b.{0,40}\bproduction\b.{0,40}\b(secret|credential|key|token|password)\b",
93
+ re.IGNORECASE,
94
+ )
95
+
96
+
97
+ def ensure_agentsecure_md(path: str = AGENTSECURE_MD, force: bool = False) -> Dict[str, Any]:
98
+ exists = os.path.exists(path)
99
+ if exists and not force:
100
+ return {"path": path, "created": False, "overwritten": False}
101
+ with open(path, "w", encoding="utf-8") as handle:
102
+ handle.write(AGENTSECURE_MD_TEMPLATE)
103
+ return {"path": path, "created": not exists, "overwritten": exists}
104
+
105
+
106
+ def validate_agentsecure_md(path: str = AGENTSECURE_MD) -> Dict[str, Any]:
107
+ errors: List[Dict[str, Any]] = []
108
+ warnings: List[Dict[str, Any]] = []
109
+ if not os.path.exists(path):
110
+ return {
111
+ "path": path,
112
+ "exists": False,
113
+ "ok": False,
114
+ "template_version": AGENTSECURE_MD_TEMPLATE_VERSION,
115
+ "errors": [{"line": 0, "code": "missing_file", "message": "AGENTSECURE.md was not found"}],
116
+ "warnings": [],
117
+ }
118
+ with open(path, "r", encoding="utf-8") as handle:
119
+ text = handle.read()
120
+ if "# AGENTSECURE.md" not in text:
121
+ warnings.append({"line": 0, "code": "missing_title", "message": "Expected '# AGENTSECURE.md' title"})
122
+ for index, line in enumerate(text.splitlines(), start=1):
123
+ _validate_line(line, index, errors)
124
+ return {
125
+ "path": path,
126
+ "exists": True,
127
+ "ok": not errors,
128
+ "template_version": AGENTSECURE_MD_TEMPLATE_VERSION,
129
+ "errors": errors,
130
+ "warnings": warnings,
131
+ }
132
+
133
+
134
+ def agentsecure_md_status(path: str = AGENTSECURE_MD) -> Dict[str, Any]:
135
+ if os.path.exists(path):
136
+ return validate_agentsecure_md(path)
137
+ return {
138
+ "path": path,
139
+ "exists": False,
140
+ "ok": False,
141
+ "template_version": AGENTSECURE_MD_TEMPLATE_VERSION,
142
+ "errors": [],
143
+ "warnings": [],
144
+ }
145
+
146
+
147
+ def _validate_line(line: str, line_number: int, errors: List[Dict[str, Any]]) -> None:
148
+ mode_match = FORBIDDEN_MODE_RE.search(line)
149
+ if mode_match:
150
+ code = "allow_real" if "real" in mode_match.group(1).lower() else "allow"
151
+ errors.append(_error(line_number, code, "Raw real-secret passthrough is not supported"))
152
+ if PRIVATE_KEY_RE.search(line):
153
+ errors.append(_error(line_number, "private_key", "Private keys must not be stored in AGENTSECURE.md"))
154
+ if ALLOW_PRODUCTION_SECRET_RE.search(line):
155
+ errors.append(_error(line_number, "allow_production_secret", "Production secrets must not be allowed directly"))
156
+ for pattern in SECRET_TOKEN_PATTERNS:
157
+ if pattern.search(line):
158
+ errors.append(_error(line_number, "raw_secret", "Secret-looking token found"))
159
+ break
160
+ assignment = SECRET_ASSIGNMENT_RE.match(line)
161
+ if assignment and _looks_like_raw_value(assignment.group(2)):
162
+ errors.append(_error(line_number, "raw_env_assignment", "Raw secret-looking .env assignment found"))
163
+
164
+
165
+ def _looks_like_raw_value(value: str) -> bool:
166
+ cleaned = value.strip().strip("\"'")
167
+ if not cleaned:
168
+ return False
169
+ lowered = cleaned.lower()
170
+ safe_markers = [
171
+ "example",
172
+ "localhost",
173
+ "dev.local",
174
+ "test-dev",
175
+ "virt_",
176
+ "[removed",
177
+ "<",
178
+ "your-",
179
+ "placeholder",
180
+ "dummy",
181
+ ]
182
+ if any(marker in lowered for marker in safe_markers):
183
+ return False
184
+ return len(cleaned) >= 8
185
+
186
+
187
+ def _error(line: int, code: str, message: str) -> Dict[str, Any]:
188
+ return {"line": line, "code": code, "message": message}
@@ -5,6 +5,7 @@ import socket
5
5
  import stat
6
6
  from typing import Any, Dict, List
7
7
 
8
+ from agentsecure.core.agentsecure_md import AGENTSECURE_MD, agentsecure_md_status, ensure_agentsecure_md
8
9
  from agentsecure.core.config import JsonConfigWriter
9
10
  from agentsecure.core.config_profiles import profile_metadata
10
11
  from agentsecure.core.time import now_seconds
@@ -54,11 +55,18 @@ class ProductService:
54
55
  handle.write("*\n!.gitignore\n")
55
56
  created.append(gitignore_path)
56
57
 
58
+ agentsecure_md = ensure_agentsecure_md(AGENTSECURE_MD, force)
59
+ if agentsecure_md["created"] or agentsecure_md["overwritten"]:
60
+ created.append(agentsecure_md["path"])
61
+
57
62
  return {
58
63
  "config_path": self.config_path,
59
64
  "config_created": config_created,
65
+ "agentsecure_md": agentsecure_md,
60
66
  "created": created,
61
67
  "next_steps": [
68
+ "review AGENTSECURE.md",
69
+ "agentsecure policy validate",
62
70
  "agentsecure discover",
63
71
  "agentsecure run --protect-all -- <agent-command>",
64
72
  "agentsecure status",
@@ -85,6 +93,7 @@ class ProductService:
85
93
  return {
86
94
  "config_path": self.config_path,
87
95
  "config_exists": os.path.exists(self.config_path),
96
+ "agentsecure_md": agentsecure_md_status(AGENTSECURE_MD),
88
97
  "configured_secrets": len(config.get("secrets", [])) if config else 0,
89
98
  "discovered_secrets": len(discoveries),
90
99
  "grants": {
@@ -106,6 +115,13 @@ class ProductService:
106
115
  def doctor(self) -> Dict[str, Any]:
107
116
  checks = []
108
117
  checks.append(self._check("config_exists", os.path.exists(self.config_path), self.config_path))
118
+ agentsecure_md = agentsecure_md_status(AGENTSECURE_MD)
119
+ checks.append(self._check("agentsecure_md_exists", agentsecure_md["exists"], AGENTSECURE_MD))
120
+ if agentsecure_md["exists"]:
121
+ detail = "AGENTSECURE.md policy guidance is valid"
122
+ if not agentsecure_md["ok"]:
123
+ detail = "; ".join(error["message"] for error in agentsecure_md["errors"]) or AGENTSECURE_MD
124
+ checks.append(self._check("agentsecure_md_valid", agentsecure_md["ok"], detail))
109
125
  checks.append(self._check("agentsecure_dir_exists", os.path.isdir(".agentsecure"), ".agentsecure"))
110
126
  checks.append(
111
127
  self._check(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentsecure
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Local-first security runtime for AI coding agents
5
5
  Home-page: https://github.com/ShellFrameAI/agentsecure-community
6
6
  Author: ShellFrame AI
@@ -76,12 +76,18 @@ Why:
76
76
 
77
77
  ## Quickstart In A Project
78
78
 
79
- Create a local config:
79
+ Create a local config and repo guidance file:
80
80
 
81
81
  ```bash
82
82
  agentsecure init
83
83
  ```
84
84
 
85
+ This creates `agentsecure.json`, local private state under `.agentsecure/`, and `AGENTSECURE.md`. Review the Markdown file before running agents:
86
+
87
+ ```bash
88
+ agentsecure policy validate
89
+ ```
90
+
85
91
  Create a fake `.env` for testing:
86
92
 
87
93
  ```bash
@@ -166,7 +172,7 @@ Command-guard mode is a usability guard, not a hard sandbox. A determined proces
166
172
 
167
173
  ## Example Policy
168
174
 
169
- See [examples/agentsecure.community.json](examples/agentsecure.community.json) and [examples/.env.example](examples/.env.example).
175
+ See [examples/agentsecure.community.json](examples/agentsecure.community.json), [examples/AGENTSECURE.md](examples/AGENTSECURE.md), and [examples/.env.example](examples/.env.example).
170
176
 
171
177
  Minimal policy shape:
172
178
 
@@ -195,6 +201,7 @@ Minimal policy shape:
195
201
 
196
202
  ```bash
197
203
  agentsecure init
204
+ agentsecure policy validate
198
205
  agentsecure status
199
206
  agentsecure doctor
200
207
  agentsecure discover
@@ -269,6 +276,12 @@ python3 scripts/secret_scan.py .
269
276
 
270
277
  CI runs tests across supported Python versions and runs the local secret scan.
271
278
 
279
+ ## AGENTSECURE.md
280
+
281
+ `AGENTSECURE.md` is a small repo-level policy guidance file for humans and AI coding agents. In the community release, AgentSecure creates it and validates that it does not contain raw secrets or unsupported raw-secret passthrough modes.
282
+
283
+ Supported community secret modes in the Markdown guidance are `virtualize` and `deny`. Do not use `allow` or `allow_real` for secrets. The Markdown file is guidance plus local validation; it is not a full sandbox by itself.
284
+
272
285
  ## Public Release Boundary
273
286
 
274
287
  This community release does not include hosted backend integration, enterprise policy sync, billing/licensing, production secrets, internal endpoints, or sensitive commercial heuristics. See [OPEN_SOURCE_PLAN.md](OPEN_SOURCE_PLAN.md) and [OPEN_SOURCE_READINESS_REPORT.md](OPEN_SOURCE_READINESS_REPORT.md) for the public/private boundary.
@@ -30,6 +30,7 @@ agentsecure/client/wrappers.py
30
30
  agentsecure/config/__init__.py
31
31
  agentsecure/config/default_agentsecure.json
32
32
  agentsecure/core/__init__.py
33
+ agentsecure/core/agentsecure_md.py
33
34
  agentsecure/core/capabilities.py
34
35
  agentsecure/core/command_metadata.py
35
36
  agentsecure/core/config.py
@@ -85,6 +86,7 @@ agentsecure/workspace/diff.py
85
86
  agentsecure/workspace/materializer.py
86
87
  agentsecure/workspace/rewriter.py
87
88
  agentsecure/workspace/strategies.py
89
+ tests/test_agentsecure_md.py
88
90
  tests/test_audit.py
89
91
  tests/test_cli.py
90
92
  tests/test_cli_demo.py
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = agentsecure
3
- version = 0.1.3
3
+ version = 0.1.4
4
4
  description = Local-first security runtime for AI coding agents
5
5
  long_description = file: README.md
6
6
  long_description_content_type = text/markdown
@@ -0,0 +1,66 @@
1
+ import os
2
+ import tempfile
3
+ import unittest
4
+
5
+ from agentsecure.core.agentsecure_md import ensure_agentsecure_md, validate_agentsecure_md
6
+
7
+
8
+ class AgentSecureMdTest(unittest.TestCase):
9
+ def test_template_is_created_and_validates(self):
10
+ with tempfile.TemporaryDirectory() as temp_dir:
11
+ cwd = os.getcwd()
12
+ try:
13
+ os.chdir(temp_dir)
14
+ result = ensure_agentsecure_md()
15
+ self.assertTrue(result["created"])
16
+ validation = validate_agentsecure_md()
17
+ self.assertTrue(validation["ok"], validation)
18
+ self.assertEqual([], validation["errors"])
19
+ finally:
20
+ os.chdir(cwd)
21
+
22
+ def test_rejects_raw_secret_assignment(self):
23
+ with tempfile.TemporaryDirectory() as temp_dir:
24
+ path = os.path.join(temp_dir, "AGENTSECURE.md")
25
+ with open(path, "w", encoding="utf-8") as handle:
26
+ handle.write("# AGENTSECURE.md\nOPENAI_API_KEY=sk-live-secret-value\n")
27
+ validation = validate_agentsecure_md(path)
28
+ self.assertFalse(validation["ok"])
29
+ self.assertIn("raw_secret", {error["code"] for error in validation["errors"]})
30
+
31
+ def test_rejects_private_key_and_allow_real(self):
32
+ with tempfile.TemporaryDirectory() as temp_dir:
33
+ path = os.path.join(temp_dir, "AGENTSECURE.md")
34
+ with open(path, "w", encoding="utf-8") as handle:
35
+ handle.write(
36
+ "# AGENTSECURE.md\n"
37
+ "mode: allow_real\n"
38
+ "-----BEGIN " + "PRIVATE KEY-----\n"
39
+ )
40
+ validation = validate_agentsecure_md(path)
41
+ codes = {error["code"] for error in validation["errors"]}
42
+ self.assertFalse(validation["ok"])
43
+ self.assertIn("allow_real", codes)
44
+ self.assertIn("private_key", codes)
45
+
46
+ def test_rejects_allow_mode(self):
47
+ with tempfile.TemporaryDirectory() as temp_dir:
48
+ path = os.path.join(temp_dir, "AGENTSECURE.md")
49
+ with open(path, "w", encoding="utf-8") as handle:
50
+ handle.write("# AGENTSECURE.md\nDATABASE_URL_DEV:\n mode: allow\n")
51
+ validation = validate_agentsecure_md(path)
52
+ self.assertFalse(validation["ok"])
53
+ self.assertIn("allow", {error["code"] for error in validation["errors"]})
54
+
55
+ def test_rejects_production_secret_direct_allow_wording(self):
56
+ with tempfile.TemporaryDirectory() as temp_dir:
57
+ path = os.path.join(temp_dir, "AGENTSECURE.md")
58
+ with open(path, "w", encoding="utf-8") as handle:
59
+ handle.write("# AGENTSECURE.md\nallow production database credential\n")
60
+ validation = validate_agentsecure_md(path)
61
+ self.assertFalse(validation["ok"])
62
+ self.assertIn("allow_production_secret", {error["code"] for error in validation["errors"]})
63
+
64
+
65
+ if __name__ == "__main__":
66
+ unittest.main()
@@ -16,7 +16,9 @@ class ProductServiceTest(unittest.TestCase):
16
16
  service = ProductService("agentsecure.json", CompositeSecretScanner([]))
17
17
  result = service.init_project()
18
18
  self.assertTrue(result["config_created"])
19
+ self.assertTrue(result["agentsecure_md"]["created"])
19
20
  self.assertTrue(os.path.exists("agentsecure.json"))
21
+ self.assertTrue(os.path.exists("AGENTSECURE.md"))
20
22
  self.assertTrue(os.path.exists(os.path.join(".agentsecure", ".gitignore")))
21
23
  with open("agentsecure.json", "r") as handle:
22
24
  config = json.load(handle)
@@ -3,6 +3,7 @@ import os
3
3
  import unittest
4
4
 
5
5
  import agentsecure
6
+ from agentsecure.cli.main import build_parser
6
7
 
7
8
 
8
9
  class VersionTest(unittest.TestCase):
@@ -12,6 +13,12 @@ class VersionTest(unittest.TestCase):
12
13
 
13
14
  self.assertEqual(parser["metadata"]["version"], agentsecure.__version__)
14
15
 
16
+ def test_cli_version_flag_uses_package_version(self):
17
+ parser = build_parser()
18
+ with self.assertRaises(SystemExit) as context:
19
+ parser.parse_args(["--version"])
20
+ self.assertEqual(0, context.exception.code)
21
+
15
22
 
16
23
  if __name__ == "__main__":
17
24
  unittest.main()
File without changes
File without changes
File without changes
File without changes