securityagent-core 4.18.0__tar.gz → 4.24.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {securityagent_core-4.18.0/src/securityagent_core.egg-info → securityagent_core-4.24.0}/PKG-INFO +4 -2
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/pyproject.toml +3 -2
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/cli.py +28 -0
- securityagent_core-4.24.0/src/endpoint_agent/scanners/active_learner.py +251 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0/src/securityagent_core.egg-info}/PKG-INFO +4 -2
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/securityagent_core.egg-info/SOURCES.txt +1 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/securityagent_core.egg-info/requires.txt +4 -1
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/compliance_report.py +177 -2
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/LICENSE +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/MANIFEST.in +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/README.md +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/setup.cfg +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/__main__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/alerting/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/alerting/alert_manager.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/alerting/console_handler.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/alerting/log_handler.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/alerting/webhook_handler.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/cloud_bridge/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/cloud_bridge/lockdown.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/config/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/config/privacy_mode.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/config/settings.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/engine.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/monitors/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/monitors/behavioral_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/monitors/file_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/monitors/honeypot_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/monitors/privilege_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/monitors/process_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/code_fingerprint.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/content_analyzer.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/credential_scanner.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/data_flow_tracker.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/dlp_scanner.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/finding_validators.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/installation_scanner.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/knowledge_graph.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/lethal_trifecta.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/llm_analyzer.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/llm_provider.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/memory_guard.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/ml_classifier.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/model_locality.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/nist_tagger.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/prompt_analyzer.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/prompt_guard.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/rule_classifier.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/shadow_ai_detector.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/text_extractor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/tool_call_guard.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/vuln_scanner.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/secure_fs.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_alert_manager.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_behavioral_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_cloud_bridge.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_credential_scanner.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_data_flow_tracker.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_dlp_scanner.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_engine.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_file_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_honeypot_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_installation_scanner.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_llm_analyzer.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_privilege_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_process_monitor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_prompt_analyzer.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_prompt_guard.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_rule_classifier.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_secure_fs.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_semantic_disclosure.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_text_extractor.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_tool_call_guard.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_trial_manager.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/trial_manager.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/plugin.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/policy/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/policy/audit.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/policy/chain_detector.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/policy/engine.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/policy/memory_bridge.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/policy/rules.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/policy/session.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/scripts/secagent_check.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/securityagent_core/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/securityagent_core.egg-info/dependency_links.txt +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/securityagent_core.egg-info/entry_points.txt +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/securityagent_core.egg-info/top_level.txt +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/adapters/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/adapters/cli_adapter.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/adapters/mcp_server.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/adapters/mcp_sse_server.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/adapters/python_sdk.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/base.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/context.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/__init__.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/analyze_prompt.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/audit_log.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/check_policy.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/get_session_policy.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/scan_output.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/secure_exec.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/secure_read.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/token_optimize.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/registry.py +0 -0
- {securityagent_core-4.18.0 → securityagent_core-4.24.0}/tests/test_prompt_scenarios.py +0 -0
{securityagent_core-4.18.0/src/securityagent_core.egg-info → securityagent_core-4.24.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: securityagent-core
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.24.0
|
|
4
4
|
Summary: Local-first DLP engine and security layer for AI coding agents. Secures Copilot, Claude Code, Cursor, Windsurf, and any MCP-compatible agent.
|
|
5
5
|
Author-email: SecureMind <founders@securemind.live>
|
|
6
6
|
License: Proprietary
|
|
@@ -41,11 +41,13 @@ Provides-Extra: ocr
|
|
|
41
41
|
Requires-Dist: PyMuPDF; extra == "ocr"
|
|
42
42
|
Requires-Dist: pytesseract; extra == "ocr"
|
|
43
43
|
Requires-Dist: Pillow; extra == "ocr"
|
|
44
|
+
Provides-Extra: pdf
|
|
45
|
+
Requires-Dist: fpdf2; extra == "pdf"
|
|
44
46
|
Provides-Extra: dev
|
|
45
47
|
Requires-Dist: pytest; extra == "dev"
|
|
46
48
|
Requires-Dist: moto; extra == "dev"
|
|
47
49
|
Provides-Extra: all
|
|
48
|
-
Requires-Dist: securityagent-core[cloud,llm,ml,ocr]; extra == "all"
|
|
50
|
+
Requires-Dist: securityagent-core[cloud,llm,ml,ocr,pdf]; extra == "all"
|
|
49
51
|
Dynamic: license-file
|
|
50
52
|
|
|
51
53
|
# securityagent-core
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "securityagent-core"
|
|
7
|
-
version = "4.
|
|
7
|
+
version = "4.24.0"
|
|
8
8
|
description = "Local-first DLP engine and security layer for AI coding agents. Secures Copilot, Claude Code, Cursor, Windsurf, and any MCP-compatible agent."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "Proprietary"}
|
|
@@ -46,8 +46,9 @@ ml = ["sentence-transformers", "scikit-learn", "joblib", "numpy"]
|
|
|
46
46
|
cloud = ["boto3", "PyJWT", "cryptography"]
|
|
47
47
|
llm = ["ollama"]
|
|
48
48
|
ocr = ["PyMuPDF", "pytesseract", "Pillow"]
|
|
49
|
+
pdf = ["fpdf2"]
|
|
49
50
|
dev = ["pytest", "moto"]
|
|
50
|
-
all = ["securityagent-core[ml,cloud,llm,ocr]"]
|
|
51
|
+
all = ["securityagent-core[ml,cloud,llm,ocr,pdf]"]
|
|
51
52
|
|
|
52
53
|
[tool.setuptools.packages.find]
|
|
53
54
|
where = ["src"]
|
|
@@ -294,6 +294,33 @@ def cmd_demo(args):
|
|
|
294
294
|
|
|
295
295
|
def cmd_train(args):
|
|
296
296
|
"""Train/retrain the ML classifier."""
|
|
297
|
+
if args.auto:
|
|
298
|
+
# Active learning: check if retraining is needed
|
|
299
|
+
print(BANNER)
|
|
300
|
+
print(" \033[1mActive Learning Check\033[0m\n")
|
|
301
|
+
from endpoint_agent.scanners.active_learner import ActiveLearner
|
|
302
|
+
learner = ActiveLearner()
|
|
303
|
+
check = learner.should_retrain()
|
|
304
|
+
print(f" New audit entries: {check['new_entries']}")
|
|
305
|
+
print(f" Total entries: {check['total_entries']}")
|
|
306
|
+
print(f" Threshold: {learner.retrain_threshold}")
|
|
307
|
+
print(f" Decision: {'RETRAIN' if check['should_retrain'] else 'SKIP'}")
|
|
308
|
+
print(f" Reason: {check['reason']}\n")
|
|
309
|
+
|
|
310
|
+
if check["should_retrain"]:
|
|
311
|
+
result = learner.check_and_retrain()
|
|
312
|
+
if result["action"] == "retrained":
|
|
313
|
+
print(f" \033[92mRetrained successfully!\033[0m")
|
|
314
|
+
print(f" Intent F1: {result['intent_f1']['old']:.3f} -> {result['intent_f1']['new']:.3f}")
|
|
315
|
+
print(f" PII F1: {result['pii_f1']['old']:.3f} -> {result['pii_f1']['new']:.3f}")
|
|
316
|
+
elif result["action"] == "rejected":
|
|
317
|
+
print(f" \033[93mNew model rejected:\033[0m {result['reason']}")
|
|
318
|
+
else:
|
|
319
|
+
print(f" \033[90m{result.get('reason', 'Unknown')}\033[0m")
|
|
320
|
+
else:
|
|
321
|
+
print(f" \033[90mNo retraining needed.\033[0m")
|
|
322
|
+
return
|
|
323
|
+
|
|
297
324
|
train_script = Path.home() / "AgnosticSecurity" / "scripts" / "train_enhanced_classifier.py"
|
|
298
325
|
|
|
299
326
|
if not train_script.exists():
|
|
@@ -429,6 +456,7 @@ Examples:
|
|
|
429
456
|
# train
|
|
430
457
|
train_p = subparsers.add_parser("train", help="Train/retrain ML classifier")
|
|
431
458
|
train_p.add_argument("--eval", action="store_true", help="Run eval after training")
|
|
459
|
+
train_p.add_argument("--auto", action="store_true", help="Active learning: auto-retrain if enough new data")
|
|
432
460
|
|
|
433
461
|
# mcp
|
|
434
462
|
mcp_p = subparsers.add_parser("mcp", help="Start MCP security server")
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Active learning pipeline — auto-retrain ML classifiers from new audit data.
|
|
2
|
+
|
|
3
|
+
Monitors hook_audit.jsonl for new entries since last training, retrains
|
|
4
|
+
when sufficient new data accumulates, and validates before promoting.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from endpoint_agent.scanners.active_learner import ActiveLearner
|
|
8
|
+
learner = ActiveLearner()
|
|
9
|
+
learner.check_and_retrain() # Called periodically or via secagent train --auto
|
|
10
|
+
|
|
11
|
+
Flow:
|
|
12
|
+
1. Load last training timestamp from model metadata
|
|
13
|
+
2. Count new audit entries since then
|
|
14
|
+
3. If >= threshold (default 200), trigger retrain
|
|
15
|
+
4. Train on full dataset (old + new)
|
|
16
|
+
5. Validate: new model must beat old model's CV F1
|
|
17
|
+
6. If passes, promote; if not, keep old model
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import time
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Dict, Optional
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
_MODEL_DIR = os.environ.get(
|
|
30
|
+
"EA_ML_MODEL_DIR",
|
|
31
|
+
os.path.join(os.path.expanduser("~"), ".agnosticsecurity", "models"),
|
|
32
|
+
)
|
|
33
|
+
_METADATA_PATH = os.path.join(_MODEL_DIR, "training_metadata.json")
|
|
34
|
+
_INTENT_MODEL_PATH = os.path.join(_MODEL_DIR, "intent_classifier_v2.joblib")
|
|
35
|
+
_PII_MODEL_PATH = os.path.join(_MODEL_DIR, "pii_context_classifier_v2.joblib")
|
|
36
|
+
|
|
37
|
+
# Default audit log locations
|
|
38
|
+
_AUDIT_PATHS = [
|
|
39
|
+
os.path.join(os.path.expanduser("~"), "AgnosticSecurity", "logs", "hook_audit.jsonl"),
|
|
40
|
+
os.path.join(os.path.expanduser("~"), ".securityagent", "skills_audit.jsonl"),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
# Minimum new entries before retraining
|
|
44
|
+
_RETRAIN_THRESHOLD = int(os.environ.get("EA_RETRAIN_THRESHOLD", "200"))
|
|
45
|
+
# Minimum F1 improvement required to promote new model
|
|
46
|
+
_MIN_F1_IMPROVEMENT = float(os.environ.get("EA_MIN_F1_IMPROVEMENT", "-0.02"))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _load_metadata() -> Dict:
|
|
50
|
+
"""Load training metadata (last train time, metrics)."""
|
|
51
|
+
if os.path.exists(_METADATA_PATH):
|
|
52
|
+
try:
|
|
53
|
+
with open(_METADATA_PATH) as f:
|
|
54
|
+
return json.load(f)
|
|
55
|
+
except (json.JSONDecodeError, IOError):
|
|
56
|
+
pass
|
|
57
|
+
return {"last_trained_at": 0, "last_entry_count": 0, "intent_f1": 0.0, "pii_f1": 0.0}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _save_metadata(metadata: Dict):
|
|
61
|
+
"""Save training metadata."""
|
|
62
|
+
os.makedirs(os.path.dirname(_METADATA_PATH), exist_ok=True)
|
|
63
|
+
with open(_METADATA_PATH, "w") as f:
|
|
64
|
+
json.dump(metadata, f, indent=2)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _count_entries_since(paths: list, since_ts: float) -> int:
|
|
68
|
+
"""Count audit log entries newer than since_ts."""
|
|
69
|
+
count = 0
|
|
70
|
+
for path in paths:
|
|
71
|
+
if not os.path.exists(path):
|
|
72
|
+
continue
|
|
73
|
+
with open(path) as f:
|
|
74
|
+
for line in f:
|
|
75
|
+
try:
|
|
76
|
+
entry = json.loads(line)
|
|
77
|
+
ts = entry.get("ts", "")
|
|
78
|
+
# Parse ISO timestamp to epoch
|
|
79
|
+
if isinstance(ts, str) and ts:
|
|
80
|
+
import datetime
|
|
81
|
+
try:
|
|
82
|
+
dt = datetime.datetime.fromisoformat(ts)
|
|
83
|
+
if dt.timestamp() > since_ts:
|
|
84
|
+
count += 1
|
|
85
|
+
except (ValueError, TypeError):
|
|
86
|
+
count += 1 # Can't parse = assume new
|
|
87
|
+
elif isinstance(ts, (int, float)) and ts > since_ts:
|
|
88
|
+
count += 1
|
|
89
|
+
except (json.JSONDecodeError, TypeError):
|
|
90
|
+
continue
|
|
91
|
+
return count
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _total_entries(paths: list) -> int:
|
|
95
|
+
"""Count total audit entries across all paths."""
|
|
96
|
+
count = 0
|
|
97
|
+
for path in paths:
|
|
98
|
+
if not os.path.exists(path):
|
|
99
|
+
continue
|
|
100
|
+
with open(path) as f:
|
|
101
|
+
for _ in f:
|
|
102
|
+
count += 1
|
|
103
|
+
return count
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class ActiveLearner:
|
|
107
|
+
"""Auto-retrain ML classifiers when new audit data accumulates."""
|
|
108
|
+
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
audit_paths: Optional[list] = None,
|
|
112
|
+
retrain_threshold: int = _RETRAIN_THRESHOLD,
|
|
113
|
+
min_f1_improvement: float = _MIN_F1_IMPROVEMENT,
|
|
114
|
+
):
|
|
115
|
+
self.audit_paths = audit_paths or _AUDIT_PATHS
|
|
116
|
+
self.retrain_threshold = retrain_threshold
|
|
117
|
+
self.min_f1_improvement = min_f1_improvement
|
|
118
|
+
|
|
119
|
+
def should_retrain(self) -> Dict:
|
|
120
|
+
"""Check if retraining is needed.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
{"should_retrain": bool, "new_entries": int, "total_entries": int,
|
|
124
|
+
"reason": str}
|
|
125
|
+
"""
|
|
126
|
+
metadata = _load_metadata()
|
|
127
|
+
last_trained = metadata.get("last_trained_at", 0)
|
|
128
|
+
new_entries = _count_entries_since(self.audit_paths, last_trained)
|
|
129
|
+
total = _total_entries(self.audit_paths)
|
|
130
|
+
|
|
131
|
+
if new_entries >= self.retrain_threshold:
|
|
132
|
+
return {
|
|
133
|
+
"should_retrain": True,
|
|
134
|
+
"new_entries": new_entries,
|
|
135
|
+
"total_entries": total,
|
|
136
|
+
"reason": f"{new_entries} new entries >= threshold {self.retrain_threshold}",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
"should_retrain": False,
|
|
141
|
+
"new_entries": new_entries,
|
|
142
|
+
"total_entries": total,
|
|
143
|
+
"reason": f"{new_entries} new entries < threshold {self.retrain_threshold}",
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
def check_and_retrain(self) -> Dict:
|
|
147
|
+
"""Check if retraining is needed and do it if so.
|
|
148
|
+
|
|
149
|
+
Returns training results or skip reason.
|
|
150
|
+
"""
|
|
151
|
+
check = self.should_retrain()
|
|
152
|
+
if not check["should_retrain"]:
|
|
153
|
+
logger.info("Active learner: skip — %s", check["reason"])
|
|
154
|
+
return {"action": "skipped", **check}
|
|
155
|
+
|
|
156
|
+
logger.info("Active learner: retraining — %s", check["reason"])
|
|
157
|
+
return self._retrain(check)
|
|
158
|
+
|
|
159
|
+
def _retrain(self, check: Dict) -> Dict:
|
|
160
|
+
"""Execute retraining with validation gate."""
|
|
161
|
+
metadata = _load_metadata()
|
|
162
|
+
old_intent_f1 = metadata.get("intent_f1", 0.0)
|
|
163
|
+
old_pii_f1 = metadata.get("pii_f1", 0.0)
|
|
164
|
+
|
|
165
|
+
# Import training functions
|
|
166
|
+
try:
|
|
167
|
+
import sys
|
|
168
|
+
# Try AgnosticSecurity training script
|
|
169
|
+
train_script = os.path.join(
|
|
170
|
+
os.path.expanduser("~"), "AgnosticSecurity", "scripts"
|
|
171
|
+
)
|
|
172
|
+
if train_script not in sys.path:
|
|
173
|
+
sys.path.insert(0, train_script)
|
|
174
|
+
|
|
175
|
+
from train_enhanced_classifier import (
|
|
176
|
+
load_audit_log,
|
|
177
|
+
load_red_team_evals,
|
|
178
|
+
generate_synthetic_benign,
|
|
179
|
+
generate_pii_training_data,
|
|
180
|
+
train_intent_model,
|
|
181
|
+
train_pii_model,
|
|
182
|
+
AUDIT_LOG,
|
|
183
|
+
EVALS_DIR,
|
|
184
|
+
)
|
|
185
|
+
except ImportError as e:
|
|
186
|
+
return {
|
|
187
|
+
"action": "error",
|
|
188
|
+
"reason": f"Training script not found: {e}",
|
|
189
|
+
**check,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Load data
|
|
193
|
+
audit_samples = load_audit_log(AUDIT_LOG)
|
|
194
|
+
red_team_samples = load_red_team_evals(EVALS_DIR)
|
|
195
|
+
synthetic = generate_synthetic_benign()
|
|
196
|
+
pii_samples = generate_pii_training_data()
|
|
197
|
+
|
|
198
|
+
intent_data = audit_samples + red_team_samples + synthetic
|
|
199
|
+
if len(intent_data) < 100:
|
|
200
|
+
return {
|
|
201
|
+
"action": "skipped",
|
|
202
|
+
"reason": f"Not enough training data ({len(intent_data)} samples)",
|
|
203
|
+
**check,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# Train new models
|
|
207
|
+
intent_result = train_intent_model(intent_data)
|
|
208
|
+
pii_result = train_pii_model(pii_samples)
|
|
209
|
+
|
|
210
|
+
new_intent_f1 = intent_result.get("cv_f1_macro", 0.0)
|
|
211
|
+
new_pii_f1 = pii_result.get("cv_f1_macro", 0.0)
|
|
212
|
+
|
|
213
|
+
# Validation gate: new model must not be significantly worse
|
|
214
|
+
intent_ok = new_intent_f1 >= (old_intent_f1 + self.min_f1_improvement)
|
|
215
|
+
pii_ok = new_pii_f1 >= (old_pii_f1 + self.min_f1_improvement)
|
|
216
|
+
|
|
217
|
+
if intent_ok and pii_ok:
|
|
218
|
+
# Promote: save metadata
|
|
219
|
+
_save_metadata({
|
|
220
|
+
"last_trained_at": time.time(),
|
|
221
|
+
"last_entry_count": check["total_entries"],
|
|
222
|
+
"intent_f1": new_intent_f1,
|
|
223
|
+
"pii_f1": new_pii_f1,
|
|
224
|
+
"intent_samples": intent_result.get("samples", 0),
|
|
225
|
+
"pii_samples": pii_result.get("samples", 0),
|
|
226
|
+
"trained_by": "active_learner",
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
"action": "retrained",
|
|
231
|
+
"intent_f1": {"old": old_intent_f1, "new": new_intent_f1},
|
|
232
|
+
"pii_f1": {"old": old_pii_f1, "new": new_pii_f1},
|
|
233
|
+
"intent_samples": intent_result.get("samples", 0),
|
|
234
|
+
"pii_samples": pii_result.get("samples", 0),
|
|
235
|
+
**check,
|
|
236
|
+
}
|
|
237
|
+
else:
|
|
238
|
+
# Reject: new model is worse
|
|
239
|
+
reasons = []
|
|
240
|
+
if not intent_ok:
|
|
241
|
+
reasons.append(f"intent F1 dropped: {old_intent_f1:.3f} -> {new_intent_f1:.3f}")
|
|
242
|
+
if not pii_ok:
|
|
243
|
+
reasons.append(f"PII F1 dropped: {old_pii_f1:.3f} -> {new_pii_f1:.3f}")
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
"action": "rejected",
|
|
247
|
+
"reason": "; ".join(reasons),
|
|
248
|
+
"intent_f1": {"old": old_intent_f1, "new": new_intent_f1},
|
|
249
|
+
"pii_f1": {"old": old_pii_f1, "new": new_pii_f1},
|
|
250
|
+
**check,
|
|
251
|
+
}
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0/src/securityagent_core.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: securityagent-core
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.24.0
|
|
4
4
|
Summary: Local-first DLP engine and security layer for AI coding agents. Secures Copilot, Claude Code, Cursor, Windsurf, and any MCP-compatible agent.
|
|
5
5
|
Author-email: SecureMind <founders@securemind.live>
|
|
6
6
|
License: Proprietary
|
|
@@ -41,11 +41,13 @@ Provides-Extra: ocr
|
|
|
41
41
|
Requires-Dist: PyMuPDF; extra == "ocr"
|
|
42
42
|
Requires-Dist: pytesseract; extra == "ocr"
|
|
43
43
|
Requires-Dist: Pillow; extra == "ocr"
|
|
44
|
+
Provides-Extra: pdf
|
|
45
|
+
Requires-Dist: fpdf2; extra == "pdf"
|
|
44
46
|
Provides-Extra: dev
|
|
45
47
|
Requires-Dist: pytest; extra == "dev"
|
|
46
48
|
Requires-Dist: moto; extra == "dev"
|
|
47
49
|
Provides-Extra: all
|
|
48
|
-
Requires-Dist: securityagent-core[cloud,llm,ml,ocr]; extra == "all"
|
|
50
|
+
Requires-Dist: securityagent-core[cloud,llm,ml,ocr,pdf]; extra == "all"
|
|
49
51
|
Dynamic: license-file
|
|
50
52
|
|
|
51
53
|
# securityagent-core
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/securityagent_core.egg-info/SOURCES.txt
RENAMED
|
@@ -26,6 +26,7 @@ src/endpoint_agent/monitors/honeypot_monitor.py
|
|
|
26
26
|
src/endpoint_agent/monitors/privilege_monitor.py
|
|
27
27
|
src/endpoint_agent/monitors/process_monitor.py
|
|
28
28
|
src/endpoint_agent/scanners/__init__.py
|
|
29
|
+
src/endpoint_agent/scanners/active_learner.py
|
|
29
30
|
src/endpoint_agent/scanners/code_fingerprint.py
|
|
30
31
|
src/endpoint_agent/scanners/content_analyzer.py
|
|
31
32
|
src/endpoint_agent/scanners/credential_scanner.py
|
|
@@ -199,10 +199,15 @@ class ComplianceReportSkill(BaseSkill):
|
|
|
199
199
|
},
|
|
200
200
|
"format": {
|
|
201
201
|
"type": "string",
|
|
202
|
-
"enum": ["json", "markdown"],
|
|
203
|
-
"description": "Output format",
|
|
202
|
+
"enum": ["json", "markdown", "pdf"],
|
|
203
|
+
"description": "Output format (pdf saves to ~/.securityagent/reports/)",
|
|
204
204
|
"default": "json",
|
|
205
205
|
},
|
|
206
|
+
"company_name": {
|
|
207
|
+
"type": "string",
|
|
208
|
+
"description": "Company name for PDF report header",
|
|
209
|
+
"default": "SecureMind",
|
|
210
|
+
},
|
|
206
211
|
},
|
|
207
212
|
}
|
|
208
213
|
|
|
@@ -258,6 +263,10 @@ class ComplianceReportSkill(BaseSkill):
|
|
|
258
263
|
|
|
259
264
|
if fmt == "markdown":
|
|
260
265
|
result_data["markdown"] = self._to_markdown(reports, stats)
|
|
266
|
+
elif fmt == "pdf":
|
|
267
|
+
company = params.get("company_name", "SecureMind")
|
|
268
|
+
pdf_path = self._to_pdf(reports, stats, company)
|
|
269
|
+
result_data["pdf_path"] = pdf_path
|
|
261
270
|
|
|
262
271
|
return SkillResult(
|
|
263
272
|
status=SkillStatus.ALLOWED,
|
|
@@ -294,3 +303,169 @@ class ComplianceReportSkill(BaseSkill):
|
|
|
294
303
|
lines.append("")
|
|
295
304
|
|
|
296
305
|
return "\n".join(lines)
|
|
306
|
+
|
|
307
|
+
def _to_pdf(self, reports: Dict, stats: Dict, company: str) -> str:
|
|
308
|
+
"""Generate PDF compliance report.
|
|
309
|
+
|
|
310
|
+
Uses fpdf2 if available, otherwise falls back to a plain-text PDF
|
|
311
|
+
built with minimal PDF spec (no external deps).
|
|
312
|
+
"""
|
|
313
|
+
report_dir = os.path.join(os.path.expanduser("~"), ".securityagent", "reports")
|
|
314
|
+
os.makedirs(report_dir, exist_ok=True)
|
|
315
|
+
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
|
316
|
+
pdf_path = os.path.join(report_dir, f"compliance_report_{timestamp}.pdf")
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
from fpdf import FPDF
|
|
320
|
+
return self._pdf_with_fpdf(reports, stats, company, pdf_path)
|
|
321
|
+
except ImportError:
|
|
322
|
+
return self._pdf_fallback(reports, stats, company, pdf_path)
|
|
323
|
+
|
|
324
|
+
@staticmethod
|
|
325
|
+
def _sanitize(text: str) -> str:
|
|
326
|
+
"""Replace unicode chars that Helvetica/latin-1 can't render."""
|
|
327
|
+
return text.encode("ascii", errors="replace").decode("ascii")
|
|
328
|
+
|
|
329
|
+
def _pdf_with_fpdf(self, reports: Dict, stats: Dict, company: str, path: str) -> str:
|
|
330
|
+
"""Generate styled PDF using fpdf2."""
|
|
331
|
+
from fpdf import FPDF
|
|
332
|
+
|
|
333
|
+
pdf = FPDF()
|
|
334
|
+
pdf.set_auto_page_break(auto=True, margin=15)
|
|
335
|
+
|
|
336
|
+
# Title page
|
|
337
|
+
pdf.add_page()
|
|
338
|
+
pdf.set_font("Helvetica", "B", 24)
|
|
339
|
+
pdf.cell(0, 20, f"{company}", new_x="LMARGIN", new_y="NEXT", align="C")
|
|
340
|
+
pdf.set_font("Helvetica", "", 16)
|
|
341
|
+
pdf.cell(0, 10, "AI Agent Security Compliance Report", new_x="LMARGIN", new_y="NEXT", align="C")
|
|
342
|
+
pdf.ln(5)
|
|
343
|
+
pdf.set_font("Helvetica", "", 11)
|
|
344
|
+
pdf.cell(0, 8, f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}", new_x="LMARGIN", new_y="NEXT", align="C")
|
|
345
|
+
pdf.cell(0, 8, f"Audit entries analyzed: {stats['total']:,}", new_x="LMARGIN", new_y="NEXT", align="C")
|
|
346
|
+
pdf.cell(0, 8, f"Actions blocked: {stats['blocked']:,}", new_x="LMARGIN", new_y="NEXT", align="C")
|
|
347
|
+
|
|
348
|
+
# Summary scores
|
|
349
|
+
pdf.ln(10)
|
|
350
|
+
pdf.set_font("Helvetica", "B", 14)
|
|
351
|
+
pdf.cell(0, 10, "Executive Summary", new_x="LMARGIN", new_y="NEXT")
|
|
352
|
+
pdf.set_font("Helvetica", "", 11)
|
|
353
|
+
|
|
354
|
+
for key, report in reports.items():
|
|
355
|
+
passed = report["controls_passed"]
|
|
356
|
+
total = report["controls_total"]
|
|
357
|
+
score = report["score"]
|
|
358
|
+
status = "PASS" if report["pass"] else "FAIL"
|
|
359
|
+
pdf.cell(0, 7, f" {report['framework']}: {passed}/{total} ({score}%) - {status}",
|
|
360
|
+
new_x="LMARGIN", new_y="NEXT")
|
|
361
|
+
|
|
362
|
+
# Detailed results per framework
|
|
363
|
+
for key, report in reports.items():
|
|
364
|
+
pdf.add_page()
|
|
365
|
+
pdf.set_font("Helvetica", "B", 16)
|
|
366
|
+
pdf.cell(0, 12, report["framework"], new_x="LMARGIN", new_y="NEXT")
|
|
367
|
+
|
|
368
|
+
passed = report["controls_passed"]
|
|
369
|
+
total = report["controls_total"]
|
|
370
|
+
score = report["score"]
|
|
371
|
+
pdf.set_font("Helvetica", "", 11)
|
|
372
|
+
pdf.cell(0, 8, f"Score: {passed}/{total} ({score}%)", new_x="LMARGIN", new_y="NEXT")
|
|
373
|
+
pdf.ln(3)
|
|
374
|
+
|
|
375
|
+
# Table header
|
|
376
|
+
pdf.set_font("Helvetica", "B", 9)
|
|
377
|
+
pdf.cell(25, 7, "Control", border=1)
|
|
378
|
+
pdf.cell(40, 7, "Name", border=1)
|
|
379
|
+
pdf.cell(15, 7, "Status", border=1)
|
|
380
|
+
pdf.cell(0, 7, "Evidence", border=1, new_x="LMARGIN", new_y="NEXT")
|
|
381
|
+
|
|
382
|
+
# Table rows
|
|
383
|
+
pdf.set_font("Helvetica", "", 8)
|
|
384
|
+
for ctrl in report["results"]:
|
|
385
|
+
pdf.cell(25, 6, ctrl["id"], border=1)
|
|
386
|
+
pdf.cell(40, 6, ctrl["name"][:25], border=1)
|
|
387
|
+
pdf.cell(15, 6, ctrl["status"], border=1)
|
|
388
|
+
evidence = self._sanitize(ctrl["evidence"][:80])
|
|
389
|
+
pdf.cell(0, 6, evidence, border=1, new_x="LMARGIN", new_y="NEXT")
|
|
390
|
+
|
|
391
|
+
# Footer
|
|
392
|
+
pdf.add_page()
|
|
393
|
+
pdf.set_font("Helvetica", "B", 14)
|
|
394
|
+
pdf.cell(0, 10, "About This Report", new_x="LMARGIN", new_y="NEXT")
|
|
395
|
+
pdf.set_font("Helvetica", "", 10)
|
|
396
|
+
pdf.multi_cell(0, 6, self._sanitize(
|
|
397
|
+
f"This report was automatically generated by {company}'s AI Agent Security Platform. "
|
|
398
|
+
"Controls are evaluated against real audit trail data from production hook enforcement. "
|
|
399
|
+
"Each control status is determined by verifying that the corresponding security mechanism "
|
|
400
|
+
"is active and has processed real events.\n\n"
|
|
401
|
+
f"Platform: SecureMind v4.18.0\n"
|
|
402
|
+
f"Engine: securityagent-core (21,092 lines)\n"
|
|
403
|
+
f"Frameworks: NIST 800-53, SOC 2 Type II, HIPAA, PCI-DSS v4.0\n"
|
|
404
|
+
f"Contact: founders@securemind.live\n"
|
|
405
|
+
f"Website: https://securemind.live"
|
|
406
|
+
))
|
|
407
|
+
|
|
408
|
+
pdf.output(path)
|
|
409
|
+
return path
|
|
410
|
+
|
|
411
|
+
def _pdf_fallback(self, reports: Dict, stats: Dict, company: str, path: str) -> str:
|
|
412
|
+
"""Generate minimal PDF without external dependencies."""
|
|
413
|
+
lines = []
|
|
414
|
+
lines.append(f"{company} - AI Agent Security Compliance Report")
|
|
415
|
+
lines.append(f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
416
|
+
lines.append(f"Audit entries: {stats['total']:,} | Blocked: {stats['blocked']:,}")
|
|
417
|
+
lines.append("")
|
|
418
|
+
|
|
419
|
+
for key, report in reports.items():
|
|
420
|
+
passed = report["controls_passed"]
|
|
421
|
+
total = report["controls_total"]
|
|
422
|
+
score = report["score"]
|
|
423
|
+
status = "PASS" if report["pass"] else "FAIL"
|
|
424
|
+
lines.append(f"{'='*60}")
|
|
425
|
+
lines.append(f"{report['framework']}: {passed}/{total} ({score}%) - {status}")
|
|
426
|
+
lines.append(f"{'='*60}")
|
|
427
|
+
for ctrl in report["results"]:
|
|
428
|
+
lines.append(f" [{ctrl['status']}] {ctrl['id']} - {ctrl['name']}")
|
|
429
|
+
lines.append(f" Evidence: {ctrl['evidence']}")
|
|
430
|
+
lines.append("")
|
|
431
|
+
|
|
432
|
+
text = "\n".join(lines)
|
|
433
|
+
|
|
434
|
+
# Minimal valid PDF
|
|
435
|
+
with open(path, "wb") as f:
|
|
436
|
+
objects = []
|
|
437
|
+
# Object 1: Catalog
|
|
438
|
+
objects.append(b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n")
|
|
439
|
+
# Object 2: Pages
|
|
440
|
+
objects.append(b"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n")
|
|
441
|
+
# Object 4: Font
|
|
442
|
+
objects.append(b"4 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Courier >>\nendobj\n")
|
|
443
|
+
# Object 5: Stream content
|
|
444
|
+
content_lines = text.encode("ascii", errors="replace")
|
|
445
|
+
stream = b"BT\n/F1 8 Tf\n36 756 Td\n10 TL\n"
|
|
446
|
+
for line in text.split("\n")[:90]: # Fit one page
|
|
447
|
+
safe = line.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)")
|
|
448
|
+
stream += f"({safe}) '\n".encode()
|
|
449
|
+
stream += b"ET\n"
|
|
450
|
+
objects.append(f"5 0 obj\n<< /Length {len(stream)} >>\nstream\n".encode() + stream + b"endstream\nendobj\n")
|
|
451
|
+
# Object 3: Page
|
|
452
|
+
objects.append(b"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 5 0 R /Resources << /Font << /F1 4 0 R >> >> >>\nendobj\n")
|
|
453
|
+
|
|
454
|
+
f.write(b"%PDF-1.4\n")
|
|
455
|
+
offsets = []
|
|
456
|
+
for obj in objects:
|
|
457
|
+
offsets.append(f.tell())
|
|
458
|
+
f.write(obj)
|
|
459
|
+
xref_offset = f.tell()
|
|
460
|
+
f.write(b"xref\n")
|
|
461
|
+
f.write(f"0 {len(objects)+1}\n".encode())
|
|
462
|
+
f.write(b"0000000000 65535 f \n")
|
|
463
|
+
for off in offsets:
|
|
464
|
+
f.write(f"{off:010d} 00000 n \n".encode())
|
|
465
|
+
f.write(b"trailer\n")
|
|
466
|
+
f.write(f"<< /Size {len(objects)+1} /Root 1 0 R >>\n".encode())
|
|
467
|
+
f.write(b"startxref\n")
|
|
468
|
+
f.write(f"{xref_offset}\n".encode())
|
|
469
|
+
f.write(b"%%EOF\n")
|
|
470
|
+
|
|
471
|
+
return path
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/alerting/__init__.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/alerting/alert_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/alerting/log_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/cloud_bridge/__init__.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/cloud_bridge/lockdown.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/config/__init__.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/config/privacy_mode.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/config/settings.py
RENAMED
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/monitors/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/monitors/file_monitor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/dlp_scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/llm_analyzer.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/llm_provider.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/memory_guard.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/ml_classifier.py
RENAMED
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/nist_tagger.py
RENAMED
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/prompt_guard.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/scanners/vuln_scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_dlp_scanner.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_engine.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
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/endpoint_agent/tests/test_secure_fs.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
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/adapters/mcp_sse_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/__init__.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/analyze_prompt.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/audit_log.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/check_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/scan_output.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/secure_exec.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/secure_read.py
RENAMED
|
File without changes
|
{securityagent_core-4.18.0 → securityagent_core-4.24.0}/src/skills/implementations/token_optimize.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|