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.
- {agentsecure-0.1.3/agentsecure.egg-info → agentsecure-0.1.4}/PKG-INFO +16 -3
- {agentsecure-0.1.3 → agentsecure-0.1.4}/README.md +15 -2
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/__init__.py +1 -1
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/main.py +7 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/policy.py +8 -1
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/project.py +9 -0
- agentsecure-0.1.4/agentsecure/core/agentsecure_md.py +188 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/product.py +16 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4/agentsecure.egg-info}/PKG-INFO +16 -3
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure.egg-info/SOURCES.txt +2 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/setup.cfg +1 -1
- agentsecure-0.1.4/tests/test_agentsecure_md.py +66 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_product.py +2 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_version.py +7 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/LICENSE +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/NOTICE +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/__main__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/api/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/api/server.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/api/services.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/common.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/demo.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/proxy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/receipts.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/secrets.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cli/settings.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/client/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/client/wrappers.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/cloud.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/config/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/config/default_agentsecure.json +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/capabilities.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/command_metadata.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/config.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/config_profiles.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/container.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/key_service.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/models.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/policy_mutation.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/policy_ports.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/policy_response.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/policy_validation.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/provider_proxy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/core/time.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/crypto/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/crypto/cipher.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/crypto/key_provider.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/commands.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/policies.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/sessions.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/daemon/supervisor.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/dotenv_scanner.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/env_scanner.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/patterns.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/scanner.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/discovery/suggestions.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/gateway/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/gateway/proxy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/command.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/network.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/sanitizer.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/guard/wrappers.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/audit.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/encrypted_secret_store.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/grant_store.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/local_secret_store.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/policy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/secret_store_factory.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/secrets.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/audit.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/grants.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/key_store.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/policy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/interfaces/secrets.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/__init__.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/apply.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/diff.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/materializer.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/rewriter.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/workspace/strategies.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure.egg-info/dependency_links.txt +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure.egg-info/entry_points.txt +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure.egg-info/top_level.txt +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/pyproject.toml +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/setup.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_audit.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_cli.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_cli_demo.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_cli_policy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_cli_proxy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_config_validation.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_diff.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_discovery.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_encrypted_secret_store.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_env_policy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_env_policy_contract.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_gateway_credentials.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_guard.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_guard_network.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_key_service.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_policy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_policy_mutation.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_provider_proxy.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_secret_scan.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_secrets.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_workspace.py +0 -0
- {agentsecure-0.1.3 → agentsecure-0.1.4}/tests/test_workspace_apply.py +0 -0
- {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
|
+
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.
|
|
@@ -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
|
+
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
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentsecure-0.1.3 → agentsecure-0.1.4}/agentsecure/implementations/encrypted_secret_store.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|