agentsecure 0.1.7__tar.gz → 0.1.8__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.7/agentsecure.egg-info → agentsecure-0.1.8}/PKG-INFO +12 -1
- {agentsecure-0.1.7 → agentsecure-0.1.8}/README.md +11 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/__init__.py +1 -1
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/main.py +209 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8/agentsecure.egg-info}/PKG-INFO +12 -1
- {agentsecure-0.1.7 → agentsecure-0.1.8}/setup.cfg +1 -1
- agentsecure-0.1.8/tests/test_secret_aliases.py +186 -0
- agentsecure-0.1.7/tests/test_secret_aliases.py +0 -95
- {agentsecure-0.1.7 → agentsecure-0.1.8}/LICENSE +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/NOTICE +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/__main__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/api/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/api/server.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/api/services.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/common.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/demo.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/policy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/project.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/proxy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/receipts.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/secrets.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cli/settings.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/client/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/client/wrappers.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/cloud.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/config/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/config/default_agentsecure.json +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/agentsecure_md.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/capabilities.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/command_metadata.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/config.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/config_profiles.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/container.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/key_service.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/models.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/policy_mutation.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/policy_ports.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/policy_response.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/policy_validation.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/product.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/provider_proxy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/runtime_bindings.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/secret_aliases.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/core/time.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/crypto/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/crypto/cipher.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/crypto/key_provider.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/daemon/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/daemon/commands.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/daemon/policies.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/daemon/sessions.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/daemon/supervisor.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/discovery/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/discovery/dotenv_scanner.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/discovery/env_scanner.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/discovery/patterns.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/discovery/scanner.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/discovery/suggestions.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/gateway/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/gateway/proxy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/guard/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/guard/command.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/guard/network.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/guard/sanitizer.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/guard/wrappers.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/implementations/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/implementations/audit.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/implementations/encrypted_secret_store.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/implementations/grant_store.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/implementations/local_secret_store.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/implementations/policy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/implementations/secret_store_factory.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/implementations/secrets.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/interfaces/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/interfaces/audit.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/interfaces/grants.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/interfaces/key_store.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/interfaces/policy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/interfaces/secrets.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/workspace/__init__.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/workspace/apply.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/workspace/diff.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/workspace/materializer.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/workspace/rewriter.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure/workspace/strategies.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure.egg-info/SOURCES.txt +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure.egg-info/dependency_links.txt +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure.egg-info/entry_points.txt +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/agentsecure.egg-info/top_level.txt +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/pyproject.toml +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/setup.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_agentsecure_md.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_audit.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_cli.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_cli_demo.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_cli_policy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_cli_proxy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_config_validation.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_diff.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_discovery.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_encrypted_secret_store.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_env_policy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_env_policy_contract.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_gateway_credentials.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_guard.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_guard_network.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_key_service.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_policy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_policy_mutation.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_product.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_provider_proxy.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_secret_scan.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_secrets.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_version.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_workspace.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/tests/test_workspace_apply.py +0 -0
- {agentsecure-0.1.7 → agentsecure-0.1.8}/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.8
|
|
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,6 +76,17 @@ python3 -m agentsecure run claude
|
|
|
76
76
|
|
|
77
77
|
Keep real secrets in one local AgentSecure vault:
|
|
78
78
|
|
|
79
|
+
```bash
|
|
80
|
+
agentsecure secrets import .env
|
|
81
|
+
agentsecure run -- claude
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`secrets import` is the easiest migration path. It scans the dotenv file, stores discovered real secret values in the local vault, assigns those aliases to the current project, writes a private backup under `~/.agentsecure/backups/`, and replaces the values in `.env` with non-secret `AGENTSECURE_ALIAS_...` placeholders.
|
|
85
|
+
|
|
86
|
+
Use `--dry-run` to preview the import, or `--keep-file` if you want to store aliases without rewriting `.env`.
|
|
87
|
+
|
|
88
|
+
For manual control, add one alias at a time:
|
|
89
|
+
|
|
79
90
|
```bash
|
|
80
91
|
printf '%s' "$DATABASE_URL" | agentsecure secrets add dev_db \
|
|
81
92
|
--env-name DATABASE_URL \
|
|
@@ -50,6 +50,17 @@ python3 -m agentsecure run claude
|
|
|
50
50
|
|
|
51
51
|
Keep real secrets in one local AgentSecure vault:
|
|
52
52
|
|
|
53
|
+
```bash
|
|
54
|
+
agentsecure secrets import .env
|
|
55
|
+
agentsecure run -- claude
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`secrets import` is the easiest migration path. It scans the dotenv file, stores discovered real secret values in the local vault, assigns those aliases to the current project, writes a private backup under `~/.agentsecure/backups/`, and replaces the values in `.env` with non-secret `AGENTSECURE_ALIAS_...` placeholders.
|
|
59
|
+
|
|
60
|
+
Use `--dry-run` to preview the import, or `--keep-file` if you want to store aliases without rewriting `.env`.
|
|
61
|
+
|
|
62
|
+
For manual control, add one alias at a time:
|
|
63
|
+
|
|
53
64
|
```bash
|
|
54
65
|
printf '%s' "$DATABASE_URL" | agentsecure secrets add dev_db \
|
|
55
66
|
--env-name DATABASE_URL \
|
|
@@ -5,6 +5,7 @@ import ipaddress
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
import queue
|
|
8
|
+
import re
|
|
8
9
|
import shutil
|
|
9
10
|
import socket
|
|
10
11
|
import subprocess
|
|
@@ -14,6 +15,7 @@ import threading
|
|
|
14
15
|
import time
|
|
15
16
|
import uuid
|
|
16
17
|
from typing import Any, Dict, List, Optional
|
|
18
|
+
from urllib.parse import urlsplit
|
|
17
19
|
from urllib.request import Request, urlopen
|
|
18
20
|
|
|
19
21
|
from agentsecure import __version__
|
|
@@ -256,6 +258,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
256
258
|
use_secret_parser = secrets_subparsers.add_parser("use", help="Assign aliases to this project")
|
|
257
259
|
use_secret_parser.add_argument("alias_ids", nargs="+")
|
|
258
260
|
use_secret_parser.add_argument("--project", default="", help="Project name for audit metadata")
|
|
261
|
+
import_secret_parser = secrets_subparsers.add_parser("import", help="Move secrets from a dotenv file into the local vault")
|
|
262
|
+
import_secret_parser.add_argument("path", nargs="?", default=".env", help="Dotenv file to import, default .env")
|
|
263
|
+
import_secret_parser.add_argument("--project", default="", help="Project name for audit metadata")
|
|
264
|
+
import_secret_parser.add_argument("--approved-host", action="append", default=[], help="Additional host allowed to receive imported secrets")
|
|
265
|
+
import_secret_parser.add_argument("--keep-file", action="store_true", help="Do not rewrite the dotenv file after importing")
|
|
266
|
+
import_secret_parser.add_argument("--no-backup", action="store_true", help="Do not store a local backup before rewriting")
|
|
267
|
+
import_secret_parser.add_argument("--dry-run", action="store_true", help="Show what would be imported without writing changes")
|
|
259
268
|
|
|
260
269
|
subparsers.add_parser("discover", help="Discover likely local secrets")
|
|
261
270
|
subparsers.add_parser("suggest", help="Suggest env and network policy for discovered secrets")
|
|
@@ -1149,6 +1158,8 @@ def handle_secret_aliases(args: argparse.Namespace) -> int:
|
|
|
1149
1158
|
return list_secret_aliases(args)
|
|
1150
1159
|
if args.secrets_command == "use":
|
|
1151
1160
|
return use_secret_aliases(args)
|
|
1161
|
+
if args.secrets_command == "import":
|
|
1162
|
+
return import_secret_aliases(args)
|
|
1152
1163
|
sys.stderr.write("agentsecure: missing secrets subcommand\n")
|
|
1153
1164
|
return 2
|
|
1154
1165
|
|
|
@@ -1242,6 +1253,204 @@ def use_secret_aliases(args: argparse.Namespace) -> int:
|
|
|
1242
1253
|
return 0
|
|
1243
1254
|
|
|
1244
1255
|
|
|
1256
|
+
def import_secret_aliases(args: argparse.Namespace) -> int:
|
|
1257
|
+
dotenv_path = os.path.abspath(args.path)
|
|
1258
|
+
if not os.path.exists(dotenv_path):
|
|
1259
|
+
sys.stderr.write("agentsecure: dotenv file not found: %s\n" % args.path)
|
|
1260
|
+
return 2
|
|
1261
|
+
source_dir = os.path.dirname(dotenv_path) or "."
|
|
1262
|
+
source_name = os.path.basename(dotenv_path)
|
|
1263
|
+
discoveries = DotenvSecretScanner(source_dir, [source_name]).scan()
|
|
1264
|
+
discoveries = [secret for secret in discoveries if secret.source == source_name]
|
|
1265
|
+
if not discoveries:
|
|
1266
|
+
print(
|
|
1267
|
+
json.dumps(
|
|
1268
|
+
{
|
|
1269
|
+
"imported": [],
|
|
1270
|
+
"dotenv": args.path,
|
|
1271
|
+
"message": "No secrets found",
|
|
1272
|
+
},
|
|
1273
|
+
indent=2,
|
|
1274
|
+
sort_keys=True,
|
|
1275
|
+
)
|
|
1276
|
+
)
|
|
1277
|
+
return 0
|
|
1278
|
+
|
|
1279
|
+
imports = []
|
|
1280
|
+
for secret in discoveries:
|
|
1281
|
+
alias_id = _alias_id_for_env_name(secret.name)
|
|
1282
|
+
approved_hosts = _approved_hosts_for_import(secret, args.approved_host)
|
|
1283
|
+
imports.append(
|
|
1284
|
+
{
|
|
1285
|
+
"alias_id": alias_id,
|
|
1286
|
+
"env_name": secret.name,
|
|
1287
|
+
"provider": secret.provider_hint,
|
|
1288
|
+
"approved_hosts": approved_hosts,
|
|
1289
|
+
"placeholder": _alias_placeholder(alias_id),
|
|
1290
|
+
}
|
|
1291
|
+
)
|
|
1292
|
+
|
|
1293
|
+
if args.dry_run:
|
|
1294
|
+
print(
|
|
1295
|
+
json.dumps(
|
|
1296
|
+
{
|
|
1297
|
+
"dotenv": args.path,
|
|
1298
|
+
"imported": imports,
|
|
1299
|
+
"would_rewrite_dotenv": not args.keep_file,
|
|
1300
|
+
"dry_run": True,
|
|
1301
|
+
},
|
|
1302
|
+
indent=2,
|
|
1303
|
+
sort_keys=True,
|
|
1304
|
+
)
|
|
1305
|
+
)
|
|
1306
|
+
return 0
|
|
1307
|
+
|
|
1308
|
+
service = _secret_alias_service(args.config)
|
|
1309
|
+
try:
|
|
1310
|
+
for secret, item in zip(discoveries, imports):
|
|
1311
|
+
service.add_alias(
|
|
1312
|
+
alias_id=item["alias_id"],
|
|
1313
|
+
real_secret=secret.value,
|
|
1314
|
+
env_name=secret.name,
|
|
1315
|
+
provider=secret.provider_hint,
|
|
1316
|
+
approved_hosts=item["approved_hosts"],
|
|
1317
|
+
name=secret.name,
|
|
1318
|
+
)
|
|
1319
|
+
assigned = service.assign_to_project(
|
|
1320
|
+
args.config,
|
|
1321
|
+
[item["alias_id"] for item in imports],
|
|
1322
|
+
project=args.project or os.path.basename(os.getcwd()) or "default",
|
|
1323
|
+
)
|
|
1324
|
+
except SecretAliasError as exc:
|
|
1325
|
+
sys.stderr.write("agentsecure: %s\n" % exc)
|
|
1326
|
+
return 2
|
|
1327
|
+
|
|
1328
|
+
backup_path = ""
|
|
1329
|
+
if not args.keep_file:
|
|
1330
|
+
if not args.no_backup:
|
|
1331
|
+
try:
|
|
1332
|
+
backup_path = _backup_dotenv_to_vault(dotenv_path, args.config)
|
|
1333
|
+
except OSError as exc:
|
|
1334
|
+
sys.stderr.write("agentsecure: failed to back up dotenv file: %s\n" % exc)
|
|
1335
|
+
return 1
|
|
1336
|
+
try:
|
|
1337
|
+
_rewrite_dotenv_with_alias_placeholders(dotenv_path, {item["env_name"]: item["placeholder"] for item in imports})
|
|
1338
|
+
except OSError as exc:
|
|
1339
|
+
sys.stderr.write("agentsecure: failed to rewrite dotenv file: %s\n" % exc)
|
|
1340
|
+
return 1
|
|
1341
|
+
|
|
1342
|
+
print(
|
|
1343
|
+
json.dumps(
|
|
1344
|
+
{
|
|
1345
|
+
"dotenv": args.path,
|
|
1346
|
+
"backup": backup_path,
|
|
1347
|
+
"rewritten": not args.keep_file,
|
|
1348
|
+
"imported": [
|
|
1349
|
+
{
|
|
1350
|
+
"alias_id": item.alias_id,
|
|
1351
|
+
"env_name": item.env_name,
|
|
1352
|
+
"provider": item.provider,
|
|
1353
|
+
"approved_hosts": item.approved_hosts,
|
|
1354
|
+
}
|
|
1355
|
+
for item in assigned
|
|
1356
|
+
],
|
|
1357
|
+
"real_secrets_stored": "local_vault",
|
|
1358
|
+
},
|
|
1359
|
+
indent=2,
|
|
1360
|
+
sort_keys=True,
|
|
1361
|
+
)
|
|
1362
|
+
)
|
|
1363
|
+
return 0
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
def _alias_id_for_env_name(env_name: str) -> str:
|
|
1367
|
+
normalized = re.sub(r"[^a-z0-9._-]+", "_", str(env_name).strip().lower())
|
|
1368
|
+
normalized = normalized.strip("._-")
|
|
1369
|
+
return normalized or "secret"
|
|
1370
|
+
|
|
1371
|
+
|
|
1372
|
+
def _alias_placeholder(alias_id: str) -> str:
|
|
1373
|
+
return "AGENTSECURE_ALIAS_%s" % re.sub(r"[^A-Z0-9]+", "_", alias_id.upper()).strip("_")
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
def _approved_hosts_for_import(secret: DiscoveredSecret, extra_hosts: List[str]) -> List[str]:
|
|
1377
|
+
hosts = []
|
|
1378
|
+
inferred = _host_from_secret_value(secret.value)
|
|
1379
|
+
if inferred:
|
|
1380
|
+
hosts.append(inferred)
|
|
1381
|
+
provider_hosts = {
|
|
1382
|
+
"openai": "api.openai.com",
|
|
1383
|
+
"anthropic": "api.anthropic.com",
|
|
1384
|
+
"github": "api.github.com",
|
|
1385
|
+
"stripe": "api.stripe.com",
|
|
1386
|
+
}
|
|
1387
|
+
provider_host = provider_hosts.get(secret.provider_hint)
|
|
1388
|
+
if provider_host:
|
|
1389
|
+
hosts.append(provider_host)
|
|
1390
|
+
hosts.extend(extra_hosts or [])
|
|
1391
|
+
result = []
|
|
1392
|
+
seen = set()
|
|
1393
|
+
for host in hosts:
|
|
1394
|
+
normalized = str(host).strip().lower().rstrip(".")
|
|
1395
|
+
if normalized and normalized not in seen:
|
|
1396
|
+
result.append(normalized)
|
|
1397
|
+
seen.add(normalized)
|
|
1398
|
+
return result
|
|
1399
|
+
|
|
1400
|
+
|
|
1401
|
+
def _host_from_secret_value(value: str) -> str:
|
|
1402
|
+
try:
|
|
1403
|
+
parsed = urlsplit(str(value).strip())
|
|
1404
|
+
except ValueError:
|
|
1405
|
+
return ""
|
|
1406
|
+
return (parsed.hostname or "").strip().lower().rstrip(".")
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
def _backup_dotenv_to_vault(dotenv_path: str, config_path: str) -> str:
|
|
1410
|
+
backup_dir = os.path.join(agentsecure_home(), "backups", project_id_for_path(config_path))
|
|
1411
|
+
os.makedirs(backup_dir, exist_ok=True)
|
|
1412
|
+
backup_path = os.path.join(
|
|
1413
|
+
backup_dir,
|
|
1414
|
+
"%s.%s.bak" % (os.path.basename(dotenv_path), time.strftime("%Y%m%d%H%M%S")),
|
|
1415
|
+
)
|
|
1416
|
+
shutil.copy2(dotenv_path, backup_path)
|
|
1417
|
+
os.chmod(backup_path, 0o600)
|
|
1418
|
+
return backup_path
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
def _rewrite_dotenv_with_alias_placeholders(dotenv_path: str, placeholders: Dict[str, str]) -> None:
|
|
1422
|
+
with open(dotenv_path, "r") as handle:
|
|
1423
|
+
lines = handle.readlines()
|
|
1424
|
+
rewritten = []
|
|
1425
|
+
for line in lines:
|
|
1426
|
+
parsed_name = _dotenv_line_name(line)
|
|
1427
|
+
if parsed_name and parsed_name in placeholders:
|
|
1428
|
+
prefix = line.split("=", 1)[0].rstrip()
|
|
1429
|
+
newline = "\n" if line.endswith("\n") else ""
|
|
1430
|
+
rewritten.append("%s=%s%s" % (prefix, placeholders[parsed_name], newline))
|
|
1431
|
+
else:
|
|
1432
|
+
rewritten.append(line)
|
|
1433
|
+
fd, temp_path = tempfile.mkstemp(prefix=".agentsecure-dotenv-", dir=os.path.dirname(dotenv_path) or ".")
|
|
1434
|
+
try:
|
|
1435
|
+
with os.fdopen(fd, "w") as handle:
|
|
1436
|
+
handle.writelines(rewritten)
|
|
1437
|
+
shutil.copymode(dotenv_path, temp_path)
|
|
1438
|
+
os.replace(temp_path, dotenv_path)
|
|
1439
|
+
finally:
|
|
1440
|
+
if os.path.exists(temp_path):
|
|
1441
|
+
os.unlink(temp_path)
|
|
1442
|
+
|
|
1443
|
+
|
|
1444
|
+
def _dotenv_line_name(line: str) -> str:
|
|
1445
|
+
stripped = line.strip()
|
|
1446
|
+
if not stripped or stripped.startswith("#") or "=" not in stripped:
|
|
1447
|
+
return ""
|
|
1448
|
+
name = stripped.split("=", 1)[0].strip()
|
|
1449
|
+
if name.startswith("export "):
|
|
1450
|
+
name = name[len("export ") :].strip()
|
|
1451
|
+
return name
|
|
1452
|
+
|
|
1453
|
+
|
|
1245
1454
|
def _secret_alias_service(config_path: str) -> SecretAliasService:
|
|
1246
1455
|
home = agentsecure_home()
|
|
1247
1456
|
return SecretAliasService(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentsecure
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
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,6 +76,17 @@ python3 -m agentsecure run claude
|
|
|
76
76
|
|
|
77
77
|
Keep real secrets in one local AgentSecure vault:
|
|
78
78
|
|
|
79
|
+
```bash
|
|
80
|
+
agentsecure secrets import .env
|
|
81
|
+
agentsecure run -- claude
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`secrets import` is the easiest migration path. It scans the dotenv file, stores discovered real secret values in the local vault, assigns those aliases to the current project, writes a private backup under `~/.agentsecure/backups/`, and replaces the values in `.env` with non-secret `AGENTSECURE_ALIAS_...` placeholders.
|
|
85
|
+
|
|
86
|
+
Use `--dry-run` to preview the import, or `--keep-file` if you want to store aliases without rewriting `.env`.
|
|
87
|
+
|
|
88
|
+
For manual control, add one alias at a time:
|
|
89
|
+
|
|
79
90
|
```bash
|
|
80
91
|
printf '%s' "$DATABASE_URL" | agentsecure secrets add dev_db \
|
|
81
92
|
--env-name DATABASE_URL \
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import unittest
|
|
5
|
+
from contextlib import redirect_stdout
|
|
6
|
+
from io import StringIO
|
|
7
|
+
|
|
8
|
+
from agentsecure.cli.main import main
|
|
9
|
+
from agentsecure.core.config import JsonConfigLoader
|
|
10
|
+
from agentsecure.core.container import Container
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SecretAliasesCliTest(unittest.TestCase):
|
|
14
|
+
def test_import_dotenv_moves_secrets_to_vault_and_rewrites_file(self):
|
|
15
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
16
|
+
project_dir = os.path.join(temp_dir, "project")
|
|
17
|
+
home_dir = os.path.join(temp_dir, "home")
|
|
18
|
+
os.makedirs(project_dir)
|
|
19
|
+
config_path = os.path.join(project_dir, "agentsecure.json")
|
|
20
|
+
env_path = os.path.join(project_dir, ".env")
|
|
21
|
+
real_url = "postgres://user:password@dev.example.invalid/mydb"
|
|
22
|
+
real_key = "sk_test_dummy_value_do_not_use"
|
|
23
|
+
with open(env_path, "w") as handle:
|
|
24
|
+
handle.write("DATABASE_URL=%s\n" % real_url)
|
|
25
|
+
handle.write("STRIPE_API_KEY=%s\n" % real_key)
|
|
26
|
+
handle.write("DEBUG=true\n")
|
|
27
|
+
|
|
28
|
+
old_cwd = os.getcwd()
|
|
29
|
+
old_home = os.environ.get("AGENTSECURE_HOME")
|
|
30
|
+
os.environ["AGENTSECURE_HOME"] = home_dir
|
|
31
|
+
try:
|
|
32
|
+
os.chdir(project_dir)
|
|
33
|
+
output = StringIO()
|
|
34
|
+
with redirect_stdout(output):
|
|
35
|
+
self.assertEqual(0, main(["--config", config_path, "secrets", "import", ".env"]))
|
|
36
|
+
|
|
37
|
+
result = json.loads(output.getvalue())
|
|
38
|
+
self.assertTrue(result["rewritten"])
|
|
39
|
+
self.assertEqual("local_vault", result["real_secrets_stored"])
|
|
40
|
+
self.assertTrue(os.path.exists(result["backup"]))
|
|
41
|
+
self.assertTrue(result["backup"].startswith(home_dir))
|
|
42
|
+
|
|
43
|
+
with open(env_path, "r") as handle:
|
|
44
|
+
dotenv_text = handle.read()
|
|
45
|
+
self.assertIn("DATABASE_URL=AGENTSECURE_ALIAS_DATABASE_URL", dotenv_text)
|
|
46
|
+
self.assertIn("STRIPE_API_KEY=AGENTSECURE_ALIAS_STRIPE_API_KEY", dotenv_text)
|
|
47
|
+
self.assertIn("DEBUG=true", dotenv_text)
|
|
48
|
+
self.assertNotIn(real_url, dotenv_text)
|
|
49
|
+
self.assertNotIn(real_key, dotenv_text)
|
|
50
|
+
|
|
51
|
+
with open(config_path, "r") as handle:
|
|
52
|
+
raw = json.load(handle)
|
|
53
|
+
raw_text = json.dumps(raw)
|
|
54
|
+
self.assertNotIn(real_url, raw_text)
|
|
55
|
+
self.assertNotIn(real_key, raw_text)
|
|
56
|
+
self.assertEqual(
|
|
57
|
+
["database_url", "stripe_api_key"],
|
|
58
|
+
[item["alias_id"] for item in raw["secret_aliases"]],
|
|
59
|
+
)
|
|
60
|
+
self.assertIn("dev.example.invalid", raw["network"]["allow_domains"])
|
|
61
|
+
self.assertIn("api.stripe.com", raw["network"]["allow_domains"])
|
|
62
|
+
|
|
63
|
+
with open(result["backup"], "r") as handle:
|
|
64
|
+
backup_text = handle.read()
|
|
65
|
+
self.assertIn(real_url, backup_text)
|
|
66
|
+
self.assertIn(real_key, backup_text)
|
|
67
|
+
finally:
|
|
68
|
+
os.chdir(old_cwd)
|
|
69
|
+
if old_home is None:
|
|
70
|
+
os.environ.pop("AGENTSECURE_HOME", None)
|
|
71
|
+
else:
|
|
72
|
+
os.environ["AGENTSECURE_HOME"] = old_home
|
|
73
|
+
|
|
74
|
+
def test_import_dotenv_dry_run_does_not_write(self):
|
|
75
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
76
|
+
project_dir = os.path.join(temp_dir, "project")
|
|
77
|
+
home_dir = os.path.join(temp_dir, "home")
|
|
78
|
+
os.makedirs(project_dir)
|
|
79
|
+
config_path = os.path.join(project_dir, "agentsecure.json")
|
|
80
|
+
env_path = os.path.join(project_dir, ".env")
|
|
81
|
+
real_url = "postgres://user:password@dev.example.invalid/mydb"
|
|
82
|
+
with open(env_path, "w") as handle:
|
|
83
|
+
handle.write("DATABASE_URL=%s\n" % real_url)
|
|
84
|
+
|
|
85
|
+
old_cwd = os.getcwd()
|
|
86
|
+
old_home = os.environ.get("AGENTSECURE_HOME")
|
|
87
|
+
os.environ["AGENTSECURE_HOME"] = home_dir
|
|
88
|
+
try:
|
|
89
|
+
os.chdir(project_dir)
|
|
90
|
+
output = StringIO()
|
|
91
|
+
with redirect_stdout(output):
|
|
92
|
+
self.assertEqual(0, main(["--config", config_path, "secrets", "import", ".env", "--dry-run"]))
|
|
93
|
+
result = json.loads(output.getvalue())
|
|
94
|
+
self.assertTrue(result["dry_run"])
|
|
95
|
+
self.assertFalse(os.path.exists(config_path))
|
|
96
|
+
with open(env_path, "r") as handle:
|
|
97
|
+
self.assertIn(real_url, handle.read())
|
|
98
|
+
finally:
|
|
99
|
+
os.chdir(old_cwd)
|
|
100
|
+
if old_home is None:
|
|
101
|
+
os.environ.pop("AGENTSECURE_HOME", None)
|
|
102
|
+
else:
|
|
103
|
+
os.environ["AGENTSECURE_HOME"] = old_home
|
|
104
|
+
|
|
105
|
+
def test_project_assignment_stores_only_alias_metadata(self):
|
|
106
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
107
|
+
project_dir = os.path.join(temp_dir, "project")
|
|
108
|
+
home_dir = os.path.join(temp_dir, "home")
|
|
109
|
+
os.makedirs(project_dir)
|
|
110
|
+
config_path = os.path.join(project_dir, "agentsecure.json")
|
|
111
|
+
old_cwd = os.getcwd()
|
|
112
|
+
old_home = os.environ.get("AGENTSECURE_HOME")
|
|
113
|
+
os.environ["AGENTSECURE_HOME"] = home_dir
|
|
114
|
+
try:
|
|
115
|
+
os.chdir(project_dir)
|
|
116
|
+
os.environ["TEST_REAL_DATABASE_URL"] = "database-secret-value-for-test"
|
|
117
|
+
with redirect_stdout(StringIO()):
|
|
118
|
+
self.assertEqual(
|
|
119
|
+
0,
|
|
120
|
+
main(
|
|
121
|
+
[
|
|
122
|
+
"--config",
|
|
123
|
+
config_path,
|
|
124
|
+
"secrets",
|
|
125
|
+
"add",
|
|
126
|
+
"dev_db",
|
|
127
|
+
"--env-name",
|
|
128
|
+
"DATABASE_URL",
|
|
129
|
+
"--provider",
|
|
130
|
+
"database",
|
|
131
|
+
"--approved-host",
|
|
132
|
+
"db.example.com",
|
|
133
|
+
"--real-secret-env",
|
|
134
|
+
"TEST_REAL_DATABASE_URL",
|
|
135
|
+
]
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
self.assertEqual(0, main(["--config", config_path, "secrets", "use", "dev_db"]))
|
|
139
|
+
|
|
140
|
+
with open(config_path, "r") as handle:
|
|
141
|
+
raw = json.load(handle)
|
|
142
|
+
self.assertEqual("dev_db", raw["secret_aliases"][0]["alias_id"])
|
|
143
|
+
self.assertNotIn("database-secret-value-for-test", json.dumps(raw))
|
|
144
|
+
self.assertIn("db.example.com", raw["network"]["allow_domains"])
|
|
145
|
+
|
|
146
|
+
config = JsonConfigLoader().load(config_path)
|
|
147
|
+
from agentsecure.core.secret_aliases import SecretAliasService, local_secret_alias_store_for_home, project_id_for_path
|
|
148
|
+
from agentsecure.implementations.grant_store import local_grant_store_for_config
|
|
149
|
+
from agentsecure.implementations.secret_store_factory import encrypted_secret_store_for_vault
|
|
150
|
+
|
|
151
|
+
class MemoryAudit:
|
|
152
|
+
def record(self, event_type, details):
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
bindings = SecretAliasService(
|
|
156
|
+
local_secret_alias_store_for_home(home_dir),
|
|
157
|
+
encrypted_secret_store_for_vault(),
|
|
158
|
+
local_grant_store_for_config(config_path),
|
|
159
|
+
MemoryAudit(),
|
|
160
|
+
).prepare_run_bindings(config.secret_aliases, "2h", project_id_for_path(config_path), "run_1")
|
|
161
|
+
container = Container.from_config_path(config_path, runtime_bindings=bindings, run_id="run_1")
|
|
162
|
+
token = bindings[0].virtual_token
|
|
163
|
+
self.assertEqual(
|
|
164
|
+
"database-secret-value-for-test",
|
|
165
|
+
container.token_resolver.resolve(
|
|
166
|
+
token,
|
|
167
|
+
{"host": "db.example.com", "project_id": container.project_id, "run_id": "run_1"},
|
|
168
|
+
),
|
|
169
|
+
)
|
|
170
|
+
self.assertIsNone(
|
|
171
|
+
container.token_resolver.resolve(
|
|
172
|
+
token,
|
|
173
|
+
{"host": "evil.example.com", "project_id": container.project_id, "run_id": "run_1"},
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
finally:
|
|
177
|
+
os.chdir(old_cwd)
|
|
178
|
+
os.environ.pop("TEST_REAL_DATABASE_URL", None)
|
|
179
|
+
if old_home is None:
|
|
180
|
+
os.environ.pop("AGENTSECURE_HOME", None)
|
|
181
|
+
else:
|
|
182
|
+
os.environ["AGENTSECURE_HOME"] = old_home
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
if __name__ == "__main__":
|
|
186
|
+
unittest.main()
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import os
|
|
3
|
-
import tempfile
|
|
4
|
-
import unittest
|
|
5
|
-
from contextlib import redirect_stdout
|
|
6
|
-
from io import StringIO
|
|
7
|
-
|
|
8
|
-
from agentsecure.cli.main import main
|
|
9
|
-
from agentsecure.core.config import JsonConfigLoader
|
|
10
|
-
from agentsecure.core.container import Container
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class SecretAliasesCliTest(unittest.TestCase):
|
|
14
|
-
def test_project_assignment_stores_only_alias_metadata(self):
|
|
15
|
-
with tempfile.TemporaryDirectory() as temp_dir:
|
|
16
|
-
project_dir = os.path.join(temp_dir, "project")
|
|
17
|
-
home_dir = os.path.join(temp_dir, "home")
|
|
18
|
-
os.makedirs(project_dir)
|
|
19
|
-
config_path = os.path.join(project_dir, "agentsecure.json")
|
|
20
|
-
old_cwd = os.getcwd()
|
|
21
|
-
old_home = os.environ.get("AGENTSECURE_HOME")
|
|
22
|
-
os.environ["AGENTSECURE_HOME"] = home_dir
|
|
23
|
-
try:
|
|
24
|
-
os.chdir(project_dir)
|
|
25
|
-
os.environ["TEST_REAL_DATABASE_URL"] = "database-secret-value-for-test"
|
|
26
|
-
with redirect_stdout(StringIO()):
|
|
27
|
-
self.assertEqual(
|
|
28
|
-
0,
|
|
29
|
-
main(
|
|
30
|
-
[
|
|
31
|
-
"--config",
|
|
32
|
-
config_path,
|
|
33
|
-
"secrets",
|
|
34
|
-
"add",
|
|
35
|
-
"dev_db",
|
|
36
|
-
"--env-name",
|
|
37
|
-
"DATABASE_URL",
|
|
38
|
-
"--provider",
|
|
39
|
-
"database",
|
|
40
|
-
"--approved-host",
|
|
41
|
-
"db.example.com",
|
|
42
|
-
"--real-secret-env",
|
|
43
|
-
"TEST_REAL_DATABASE_URL",
|
|
44
|
-
]
|
|
45
|
-
),
|
|
46
|
-
)
|
|
47
|
-
self.assertEqual(0, main(["--config", config_path, "secrets", "use", "dev_db"]))
|
|
48
|
-
|
|
49
|
-
with open(config_path, "r") as handle:
|
|
50
|
-
raw = json.load(handle)
|
|
51
|
-
self.assertEqual("dev_db", raw["secret_aliases"][0]["alias_id"])
|
|
52
|
-
self.assertNotIn("database-secret-value-for-test", json.dumps(raw))
|
|
53
|
-
self.assertIn("db.example.com", raw["network"]["allow_domains"])
|
|
54
|
-
|
|
55
|
-
config = JsonConfigLoader().load(config_path)
|
|
56
|
-
from agentsecure.core.secret_aliases import SecretAliasService, local_secret_alias_store_for_home, project_id_for_path
|
|
57
|
-
from agentsecure.implementations.grant_store import local_grant_store_for_config
|
|
58
|
-
from agentsecure.implementations.secret_store_factory import encrypted_secret_store_for_vault
|
|
59
|
-
|
|
60
|
-
class MemoryAudit:
|
|
61
|
-
def record(self, event_type, details):
|
|
62
|
-
pass
|
|
63
|
-
|
|
64
|
-
bindings = SecretAliasService(
|
|
65
|
-
local_secret_alias_store_for_home(home_dir),
|
|
66
|
-
encrypted_secret_store_for_vault(),
|
|
67
|
-
local_grant_store_for_config(config_path),
|
|
68
|
-
MemoryAudit(),
|
|
69
|
-
).prepare_run_bindings(config.secret_aliases, "2h", project_id_for_path(config_path), "run_1")
|
|
70
|
-
container = Container.from_config_path(config_path, runtime_bindings=bindings, run_id="run_1")
|
|
71
|
-
token = bindings[0].virtual_token
|
|
72
|
-
self.assertEqual(
|
|
73
|
-
"database-secret-value-for-test",
|
|
74
|
-
container.token_resolver.resolve(
|
|
75
|
-
token,
|
|
76
|
-
{"host": "db.example.com", "project_id": container.project_id, "run_id": "run_1"},
|
|
77
|
-
),
|
|
78
|
-
)
|
|
79
|
-
self.assertIsNone(
|
|
80
|
-
container.token_resolver.resolve(
|
|
81
|
-
token,
|
|
82
|
-
{"host": "evil.example.com", "project_id": container.project_id, "run_id": "run_1"},
|
|
83
|
-
)
|
|
84
|
-
)
|
|
85
|
-
finally:
|
|
86
|
-
os.chdir(old_cwd)
|
|
87
|
-
os.environ.pop("TEST_REAL_DATABASE_URL", None)
|
|
88
|
-
if old_home is None:
|
|
89
|
-
os.environ.pop("AGENTSECURE_HOME", None)
|
|
90
|
-
else:
|
|
91
|
-
os.environ["AGENTSECURE_HOME"] = old_home
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if __name__ == "__main__":
|
|
95
|
-
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentsecure-0.1.7 → agentsecure-0.1.8}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|