agentsecure 0.1.0__py3-none-any.whl
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/__init__.py +4 -0
- agentsecure/__main__.py +5 -0
- agentsecure/api/__init__.py +1 -0
- agentsecure/api/server.py +8 -0
- agentsecure/api/services.py +3 -0
- agentsecure/cli/__init__.py +2 -0
- agentsecure/cli/common.py +116 -0
- agentsecure/cli/demo.py +97 -0
- agentsecure/cli/main.py +912 -0
- agentsecure/cli/policy.py +47 -0
- agentsecure/cli/project.py +137 -0
- agentsecure/cli/secrets.py +293 -0
- agentsecure/cli/settings.py +112 -0
- agentsecure/client/__init__.py +1 -0
- agentsecure/client/wrappers.py +87 -0
- agentsecure/cloud.py +30 -0
- agentsecure/core/__init__.py +2 -0
- agentsecure/core/capabilities.py +109 -0
- agentsecure/core/command_metadata.py +13 -0
- agentsecure/core/config.py +173 -0
- agentsecure/core/config_profiles.py +192 -0
- agentsecure/core/container.py +75 -0
- agentsecure/core/key_service.py +140 -0
- agentsecure/core/models.py +183 -0
- agentsecure/core/policy_mutation.py +127 -0
- agentsecure/core/policy_ports.py +61 -0
- agentsecure/core/policy_response.py +68 -0
- agentsecure/core/policy_validation.py +97 -0
- agentsecure/core/product.py +267 -0
- agentsecure/core/time.py +38 -0
- agentsecure/crypto/__init__.py +2 -0
- agentsecure/crypto/cipher.py +57 -0
- agentsecure/crypto/key_provider.py +39 -0
- agentsecure/daemon/__init__.py +1 -0
- agentsecure/daemon/commands.py +8 -0
- agentsecure/daemon/policies.py +3 -0
- agentsecure/daemon/sessions.py +3 -0
- agentsecure/daemon/supervisor.py +3 -0
- agentsecure/discovery/__init__.py +2 -0
- agentsecure/discovery/dotenv_scanner.py +60 -0
- agentsecure/discovery/env_scanner.py +29 -0
- agentsecure/discovery/patterns.py +90 -0
- agentsecure/discovery/scanner.py +27 -0
- agentsecure/discovery/suggestions.py +154 -0
- agentsecure/gateway/__init__.py +2 -0
- agentsecure/gateway/proxy.py +272 -0
- agentsecure/guard/__init__.py +2 -0
- agentsecure/guard/command.py +62 -0
- agentsecure/guard/network.py +92 -0
- agentsecure/guard/sanitizer.py +105 -0
- agentsecure/guard/wrappers.py +50 -0
- agentsecure/implementations/__init__.py +2 -0
- agentsecure/implementations/audit.py +53 -0
- agentsecure/implementations/encrypted_secret_store.py +73 -0
- agentsecure/implementations/grant_store.py +84 -0
- agentsecure/implementations/local_secret_store.py +53 -0
- agentsecure/implementations/policy.py +126 -0
- agentsecure/implementations/secret_store_factory.py +21 -0
- agentsecure/implementations/secrets.py +138 -0
- agentsecure/interfaces/__init__.py +2 -0
- agentsecure/interfaces/audit.py +11 -0
- agentsecure/interfaces/grants.py +23 -0
- agentsecure/interfaces/key_store.py +15 -0
- agentsecure/interfaces/policy.py +24 -0
- agentsecure/interfaces/secrets.py +19 -0
- agentsecure/workspace/__init__.py +2 -0
- agentsecure/workspace/apply.py +141 -0
- agentsecure/workspace/diff.py +104 -0
- agentsecure/workspace/materializer.py +112 -0
- agentsecure/workspace/rewriter.py +54 -0
- agentsecure/workspace/strategies.py +140 -0
- agentsecure-0.1.0.dist-info/METADATA +181 -0
- agentsecure-0.1.0.dist-info/RECORD +78 -0
- agentsecure-0.1.0.dist-info/WHEEL +5 -0
- agentsecure-0.1.0.dist-info/entry_points.txt +2 -0
- agentsecure-0.1.0.dist-info/licenses/LICENSE +68 -0
- agentsecure-0.1.0.dist-info/licenses/NOTICE +4 -0
- agentsecure-0.1.0.dist-info/top_level.txt +1 -0
agentsecure/__init__.py
ADDED
agentsecure/__main__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Community stub package for private/local API integrations."""
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from agentsecure.core.config import JsonConfigWriter
|
|
7
|
+
from agentsecure.core.key_service import KeyManagementError
|
|
8
|
+
from agentsecure.core.product import ProductService
|
|
9
|
+
from agentsecure.discovery.dotenv_scanner import DotenvSecretScanner
|
|
10
|
+
from agentsecure.discovery.env_scanner import EnvironmentSecretScanner
|
|
11
|
+
from agentsecure.discovery.patterns import mask_secret
|
|
12
|
+
from agentsecure.discovery.scanner import CompositeSecretScanner
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def scanner() -> CompositeSecretScanner:
|
|
16
|
+
return CompositeSecretScanner(
|
|
17
|
+
[
|
|
18
|
+
EnvironmentSecretScanner(),
|
|
19
|
+
DotenvSecretScanner(os.getcwd()),
|
|
20
|
+
]
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def load_config_data(config_path: str):
|
|
25
|
+
if not os.path.exists(config_path):
|
|
26
|
+
ProductService(config_path, scanner()).init_project()
|
|
27
|
+
with open(config_path, "r") as handle:
|
|
28
|
+
return json.load(handle)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def normalize_policy_path(path: str) -> str:
|
|
32
|
+
return os.path.normpath(path).lstrip(os.sep)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def normalize_domain(domain: str) -> str:
|
|
36
|
+
return domain.strip().lower().rstrip(".")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def print_discovered(discovered) -> None:
|
|
40
|
+
if not discovered:
|
|
41
|
+
print("No likely secrets found.", flush=True)
|
|
42
|
+
return
|
|
43
|
+
print("AgentSecure found possible secrets:", flush=True)
|
|
44
|
+
for index, secret in enumerate(discovered, 1):
|
|
45
|
+
print(
|
|
46
|
+
"[%s] %s from %s provider=%s value=%s"
|
|
47
|
+
% (index, secret.name, secret.source, secret.provider_hint, mask_secret(secret.value)),
|
|
48
|
+
flush=True,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def selected_indexes(value: str, count: int) -> List[int]:
|
|
53
|
+
if value == "all":
|
|
54
|
+
return list(range(count))
|
|
55
|
+
indexes = []
|
|
56
|
+
for part in value.split(","):
|
|
57
|
+
part = part.strip()
|
|
58
|
+
if not part:
|
|
59
|
+
continue
|
|
60
|
+
number = int(part)
|
|
61
|
+
if number < 1 or number > count:
|
|
62
|
+
raise KeyManagementError("selection out of range: %s" % number)
|
|
63
|
+
indexes.append(number - 1)
|
|
64
|
+
return indexes
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def update_protected_files(config_path: str, paths: List[str], add: bool) -> int:
|
|
68
|
+
config = load_config_data(config_path)
|
|
69
|
+
files = config.setdefault("files", {})
|
|
70
|
+
protected = list(files.get("protect_write", []))
|
|
71
|
+
if add:
|
|
72
|
+
for path in paths:
|
|
73
|
+
normalized = normalize_policy_path(path)
|
|
74
|
+
if normalized not in protected:
|
|
75
|
+
protected.append(normalized)
|
|
76
|
+
else:
|
|
77
|
+
remove = set(normalize_policy_path(path) for path in paths)
|
|
78
|
+
protected = [path for path in protected if path not in remove]
|
|
79
|
+
files["protect_write"] = protected
|
|
80
|
+
JsonConfigWriter().save(config_path, config)
|
|
81
|
+
print("Protected write paths:")
|
|
82
|
+
for path in protected:
|
|
83
|
+
print(" %s" % path)
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def update_allowed_domains(config_path: str, domains: List[str], add: bool) -> int:
|
|
88
|
+
config = load_config_data(config_path)
|
|
89
|
+
network = config.setdefault("network", {})
|
|
90
|
+
allowed = list(network.get("allow_domains", []))
|
|
91
|
+
if add:
|
|
92
|
+
for domain in domains:
|
|
93
|
+
normalized = normalize_domain(domain)
|
|
94
|
+
if normalized and normalized not in allowed:
|
|
95
|
+
allowed.append(normalized)
|
|
96
|
+
else:
|
|
97
|
+
remove = set(normalize_domain(domain) for domain in domains)
|
|
98
|
+
allowed = [domain for domain in allowed if domain not in remove]
|
|
99
|
+
network["allow_domains"] = allowed
|
|
100
|
+
JsonConfigWriter().save(config_path, config)
|
|
101
|
+
print("Allowed credential domains:")
|
|
102
|
+
for domain in allowed:
|
|
103
|
+
print(" %s" % domain)
|
|
104
|
+
return 0
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def cloud_features_enabled() -> bool:
|
|
108
|
+
return os.environ.get("AGENTSECURE_ENABLE_CLOUD", "").strip() == "1"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def cloud_features_disabled() -> int:
|
|
112
|
+
sys.stderr.write(
|
|
113
|
+
"agentsecure: cloud features are not enabled in community mode "
|
|
114
|
+
"(set AGENTSECURE_ENABLE_CLOUD=1 in private builds)\n"
|
|
115
|
+
)
|
|
116
|
+
return 2
|
agentsecure/cli/demo.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import tempfile
|
|
6
|
+
|
|
7
|
+
from agentsecure.cli.common import load_config_data, scanner
|
|
8
|
+
from agentsecure.core.config import JsonConfigWriter
|
|
9
|
+
from agentsecure.core.key_service import KeyManagementService
|
|
10
|
+
from agentsecure.core.product import ProductService
|
|
11
|
+
from agentsecure.guard.sanitizer import SecretOutputSanitizer
|
|
12
|
+
from agentsecure.implementations.audit import JsonLineAuditLogger
|
|
13
|
+
from agentsecure.implementations.grant_store import LocalJsonGrantStore
|
|
14
|
+
from agentsecure.implementations.secret_store_factory import encrypted_secret_store_for_config
|
|
15
|
+
from agentsecure.workspace.materializer import make_tree_writable
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def run_demo(args: argparse.Namespace) -> int:
|
|
19
|
+
demo_dir = tempfile.mkdtemp(prefix="agentsecure-demo-")
|
|
20
|
+
current = os.getcwd()
|
|
21
|
+
try:
|
|
22
|
+
os.chdir(demo_dir)
|
|
23
|
+
config_path = os.path.join(demo_dir, "agentsecure.json")
|
|
24
|
+
env_path = os.path.join(demo_dir, ".env")
|
|
25
|
+
openai_secret = "sk-demo-local-secret-do-not-use"
|
|
26
|
+
database_secret = "postgres://demo:demo-password@production.example/app"
|
|
27
|
+
with open(env_path, "w") as handle:
|
|
28
|
+
handle.write("OPENAI_API_KEY=%s\n" % openai_secret)
|
|
29
|
+
handle.write("DATABASE_URL_PROD=%s\n" % database_secret)
|
|
30
|
+
|
|
31
|
+
ProductService(config_path, scanner()).init_project(force=True)
|
|
32
|
+
service = KeyManagementService(
|
|
33
|
+
config_path,
|
|
34
|
+
encrypted_secret_store_for_config(config_path),
|
|
35
|
+
LocalJsonGrantStore(os.path.join(demo_dir, ".agentsecure", "grants.json")),
|
|
36
|
+
JsonLineAuditLogger(os.path.join(demo_dir, ".agentsecure", "audit.log")),
|
|
37
|
+
)
|
|
38
|
+
openai_result = service.create_key(
|
|
39
|
+
env_name="OPENAI_API_KEY",
|
|
40
|
+
real_secret=openai_secret,
|
|
41
|
+
provider="openai",
|
|
42
|
+
ttl="2h",
|
|
43
|
+
)
|
|
44
|
+
service.create_key(
|
|
45
|
+
env_name="DATABASE_URL_PROD",
|
|
46
|
+
real_secret=database_secret,
|
|
47
|
+
provider="database",
|
|
48
|
+
ttl="2h",
|
|
49
|
+
)
|
|
50
|
+
config = load_config_data(config_path)
|
|
51
|
+
config.setdefault("env_policy", {})["DATABASE_URL_PROD"] = {
|
|
52
|
+
"mode": "deny",
|
|
53
|
+
"environment": "production",
|
|
54
|
+
"risk": "high",
|
|
55
|
+
"reason": "production database credentials are not exposed to local agents",
|
|
56
|
+
}
|
|
57
|
+
JsonConfigWriter().save(config_path, config)
|
|
58
|
+
|
|
59
|
+
raw_output = _demo_read_dotenv(demo_dir)
|
|
60
|
+
sanitizer = SecretOutputSanitizer.from_config_path(config_path)
|
|
61
|
+
agent_visible = sanitizer.sanitize_text(raw_output)
|
|
62
|
+
|
|
63
|
+
print("AgentSecure community demo (local only)")
|
|
64
|
+
print("Project: %s" % demo_dir)
|
|
65
|
+
print("Command: cat .env")
|
|
66
|
+
print("Decision: mask OPENAI_API_KEY and block DATABASE_URL_PROD")
|
|
67
|
+
print("")
|
|
68
|
+
print("Agent-visible output:")
|
|
69
|
+
print(agent_visible, end="" if agent_visible.endswith("\n") else "\n")
|
|
70
|
+
print("")
|
|
71
|
+
print("Why:")
|
|
72
|
+
print(" OPENAI_API_KEY was replaced with %s" % openai_result["virtual_token"])
|
|
73
|
+
print(" DATABASE_URL_PROD was removed because env_policy sets mode=deny")
|
|
74
|
+
print(" Real secret values stayed local in the demo project")
|
|
75
|
+
print(" No cloud service, billing service, or enterprise policy sync was used")
|
|
76
|
+
if args.keep:
|
|
77
|
+
print("")
|
|
78
|
+
print("Kept demo project: %s" % demo_dir)
|
|
79
|
+
return 0
|
|
80
|
+
finally:
|
|
81
|
+
os.chdir(current)
|
|
82
|
+
if not args.keep:
|
|
83
|
+
make_tree_writable(demo_dir)
|
|
84
|
+
shutil.rmtree(demo_dir, ignore_errors=True)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _demo_read_dotenv(demo_dir: str) -> str:
|
|
88
|
+
try:
|
|
89
|
+
return subprocess.check_output(
|
|
90
|
+
["cat", ".env"],
|
|
91
|
+
cwd=demo_dir,
|
|
92
|
+
stderr=subprocess.STDOUT,
|
|
93
|
+
universal_newlines=True,
|
|
94
|
+
)
|
|
95
|
+
except (OSError, subprocess.SubprocessError):
|
|
96
|
+
with open(os.path.join(demo_dir, ".env"), "r") as handle:
|
|
97
|
+
return handle.read()
|