higpertext-cli 0.8.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.
- config/adapters_config.json +450 -0
- config/antigravity_agent_template.json +31 -0
- config/app_config.json +174 -0
- config/context_engine.json +33 -0
- config/environments/model_defaults.json +5 -0
- config/governance/branching_strategy.json +36 -0
- config/governance/deployment_gates.json +30 -0
- config/governance/guidelines_contract.json +54 -0
- config/governance/quality_gates.json +39 -0
- config/governance/section_rules.json +22 -0
- config/governance/security_guardrails.json +52 -0
- config/hooks/README.md +35 -0
- config/hooks/custom/test_output_limiter.json +9 -0
- config/hooks/global/session_prompt.json +9 -0
- config/htx_config.json +24 -0
- config/profile_learner.json +18 -0
- config/profiles/base_agent.json +40 -0
- config/profiles/base_auditor.json +19 -0
- config/profiles/base_developer.json +19 -0
- config/profiles/base_operator.json +16 -0
- config/profiles/global.json +33 -0
- config/profiles/software_developer.json +23 -0
- config/router_content.json +137 -0
- config/semantic_graph.json +66 -0
- config/workflows/ado_release_flow.json +38 -0
- config/workflows/docs-update.json +33 -0
- config/workflows/governance-check.yaml +26 -0
- config/workflows/guidelines-sync.json +40 -0
- config/workflows/higpertext-build.json +73 -0
- config/workflows/higpertext-plan.json +38 -0
- config/workflows/higpertext-review.json +41 -0
- config/workflows/pr-quality-check.json +56 -0
- config/workflows/quality-remediation.json +57 -0
- higpertext/__init__.py +18 -0
- higpertext/adapters/__init__.py +27 -0
- higpertext/adapters/adapter_utils.py +604 -0
- higpertext/adapters/claude_adapter/__init__.py +0 -0
- higpertext/adapters/claude_adapter/claude_adapter.py +154 -0
- higpertext/adapters/copilot_adapter/__init__.py +0 -0
- higpertext/adapters/copilot_adapter/copilot_adapter.py +231 -0
- higpertext/adapters/gemini_adapter/__init__.py +0 -0
- higpertext/adapters/gemini_adapter/gemini_adapter.py +211 -0
- higpertext/adapters/llm_formatter.py +46 -0
- higpertext/adapters/open_code_adapter/__init__.py +0 -0
- higpertext/adapters/open_code_adapter/open_code_adapter.py +480 -0
- higpertext/capabilities/capabilities_runner.py +216 -0
- higpertext/capabilities/common/agent-builder.json +54 -0
- higpertext/capabilities/common/agent-sync.json +34 -0
- higpertext/capabilities/common/code-skeletonizer.json +35 -0
- higpertext/capabilities/common/commit-report.json +42 -0
- higpertext/capabilities/common/context-assembler.json +37 -0
- higpertext/capabilities/common/context-budget-report.json +15 -0
- higpertext/capabilities/common/dep-manager.json +43 -0
- higpertext/capabilities/common/docs-sync.json +14 -0
- higpertext/capabilities/common/doctor.json +18 -0
- higpertext/capabilities/common/efficiency-meter.json +31 -0
- higpertext/capabilities/common/env-catalog.json +13 -0
- higpertext/capabilities/common/env-clean.json +14 -0
- higpertext/capabilities/common/env-logs.json +16 -0
- higpertext/capabilities/common/env-runner.json +23 -0
- higpertext/capabilities/common/env-status.json +13 -0
- higpertext/capabilities/common/env-stop.json +14 -0
- higpertext/capabilities/common/env-template.json +14 -0
- higpertext/capabilities/common/error-context-locator.json +23 -0
- higpertext/capabilities/common/eval-agent.json +33 -0
- higpertext/capabilities/common/file-map.json +17 -0
- higpertext/capabilities/common/governance-exception.json +54 -0
- higpertext/capabilities/common/graph-query.json +59 -0
- higpertext/capabilities/common/graph-rebuild.json +31 -0
- higpertext/capabilities/common/graph-visualize.json +37 -0
- higpertext/capabilities/common/grep-search.json +176 -0
- higpertext/capabilities/common/higpertext-tester.json +25 -0
- higpertext/capabilities/common/hook-health.json +19 -0
- higpertext/capabilities/common/hook-sync-check.json +19 -0
- higpertext/capabilities/common/hooks-manager.json +55 -0
- higpertext/capabilities/common/knowledge-asker.json +27 -0
- higpertext/capabilities/common/list-rules.json +27 -0
- higpertext/capabilities/common/llm-invoke.json +59 -0
- higpertext/capabilities/common/load-rules.json +37 -0
- higpertext/capabilities/common/memory-manager.json +65 -0
- higpertext/capabilities/common/quality-scan.json +21 -0
- higpertext/capabilities/common/quality-updater.json +35 -0
- higpertext/capabilities/common/rag-index.json +17 -0
- higpertext/capabilities/common/report-viewer.json +24 -0
- higpertext/capabilities/common/roadmap-report.json +37 -0
- higpertext/capabilities/common/scripts/_env_cli.py +65 -0
- higpertext/capabilities/common/scripts/agent_builder.py +60 -0
- higpertext/capabilities/common/scripts/agent_sync.py +56 -0
- higpertext/capabilities/common/scripts/ask_higpertext.py +38 -0
- higpertext/capabilities/common/scripts/code_skeletonizer.py +225 -0
- higpertext/capabilities/common/scripts/commit_report.py +134 -0
- higpertext/capabilities/common/scripts/context_assembler.py +70 -0
- higpertext/capabilities/common/scripts/context_budget_report.py +53 -0
- higpertext/capabilities/common/scripts/dep_manager.py +81 -0
- higpertext/capabilities/common/scripts/docs_sync.py +981 -0
- higpertext/capabilities/common/scripts/doctor.py +144 -0
- higpertext/capabilities/common/scripts/efficiency_meter.py +83 -0
- higpertext/capabilities/common/scripts/env_catalog.py +47 -0
- higpertext/capabilities/common/scripts/env_clean.py +30 -0
- higpertext/capabilities/common/scripts/env_logs.py +32 -0
- higpertext/capabilities/common/scripts/env_runner.py +53 -0
- higpertext/capabilities/common/scripts/env_status.py +38 -0
- higpertext/capabilities/common/scripts/env_stop.py +30 -0
- higpertext/capabilities/common/scripts/env_template.py +73 -0
- higpertext/capabilities/common/scripts/error_context_locator.py +138 -0
- higpertext/capabilities/common/scripts/eval_agent.py +80 -0
- higpertext/capabilities/common/scripts/file_map.py +95 -0
- higpertext/capabilities/common/scripts/governance_exception.py +116 -0
- higpertext/capabilities/common/scripts/graph_query.py +104 -0
- higpertext/capabilities/common/scripts/graph_rebuild.py +107 -0
- higpertext/capabilities/common/scripts/graph_visualize.py +76 -0
- higpertext/capabilities/common/scripts/grep_search.py +648 -0
- higpertext/capabilities/common/scripts/higpertext_tester.py +102 -0
- higpertext/capabilities/common/scripts/hook_health.py +149 -0
- higpertext/capabilities/common/scripts/hook_sync_check.py +134 -0
- higpertext/capabilities/common/scripts/hooks_manager.py +171 -0
- higpertext/capabilities/common/scripts/list_rules.py +175 -0
- higpertext/capabilities/common/scripts/llm_invoke.py +135 -0
- higpertext/capabilities/common/scripts/load_rules.py +379 -0
- higpertext/capabilities/common/scripts/memory_manager.py +210 -0
- higpertext/capabilities/common/scripts/presentation_engine.py +63 -0
- higpertext/capabilities/common/scripts/quality_scan.py +132 -0
- higpertext/capabilities/common/scripts/rag_index.py +39 -0
- higpertext/capabilities/common/scripts/report_viewer.py +106 -0
- higpertext/capabilities/common/scripts/roadmap_report.py +73 -0
- higpertext/capabilities/common/scripts/search_router.py +111 -0
- higpertext/capabilities/common/scripts/semantic_diff.py +166 -0
- higpertext/capabilities/common/scripts/semantic_search.py +43 -0
- higpertext/capabilities/common/scripts/session_control.py +136 -0
- higpertext/capabilities/common/scripts/smart_read.py +232 -0
- higpertext/capabilities/common/scripts/subagent_executor.py +143 -0
- higpertext/capabilities/common/scripts/sync_agents.py +353 -0
- higpertext/capabilities/common/scripts/task_decomposer.py +78 -0
- higpertext/capabilities/common/scripts/telemetry_report.py +36 -0
- higpertext/capabilities/common/search-router.json +24 -0
- higpertext/capabilities/common/semantic-diff.json +40 -0
- higpertext/capabilities/common/semantic-search.json +19 -0
- higpertext/capabilities/common/session-clean.json +20 -0
- higpertext/capabilities/common/session-start.json +44 -0
- higpertext/capabilities/common/smart-read.json +28 -0
- higpertext/capabilities/common/subagent-executor.json +25 -0
- higpertext/capabilities/common/sync-agents.json +32 -0
- higpertext/capabilities/common/task-decomposer.json +37 -0
- higpertext/capabilities/common/telemetry-report.json +23 -0
- higpertext/capabilities/git/__init__.py +0 -0
- higpertext/capabilities/git/committer.json +61 -0
- higpertext/capabilities/git/diff.json +33 -0
- higpertext/capabilities/git/ls-files.json +44 -0
- higpertext/capabilities/git/rm.json +27 -0
- higpertext/capabilities/git/scripts/__init__.py +0 -0
- higpertext/capabilities/git/scripts/commit_changes.py +1077 -0
- higpertext/capabilities/git/scripts/git_diff.py +171 -0
- higpertext/capabilities/git/scripts/git_ls_files.py +376 -0
- higpertext/capabilities/git/scripts/git_rm.py +62 -0
- higpertext/capabilities/security/k8s-auditor.json +33 -0
- higpertext/capabilities/security/scripts/k8s_auditor.py +307 -0
- higpertext/capabilities/security/scripts/secret_scanner.py +235 -0
- higpertext/capabilities/security/secret-scanner.json +32 -0
- higpertext/hooks/__init__.py +28 -0
- higpertext/hooks/_compat.py +27 -0
- higpertext/hooks/hook_tasks/__init__.py +1 -0
- higpertext/hooks/hook_tasks/_rules/__init__.py +0 -0
- higpertext/hooks/hook_tasks/_rules/bash_rules.py +635 -0
- higpertext/hooks/hook_tasks/_rules/context_engine_rule.py +79 -0
- higpertext/hooks/hook_tasks/_rules/context_rules.py +199 -0
- higpertext/hooks/hook_tasks/_rules/governance_adapter.py +72 -0
- higpertext/hooks/hook_tasks/_rules/profile_rules.json +25 -0
- higpertext/hooks/hook_tasks/_rules/quality_rules.py +86 -0
- higpertext/hooks/hook_tasks/_rules/security_rules.py +214 -0
- higpertext/hooks/hook_tasks/_rules/session_rules.py +316 -0
- higpertext/hooks/hook_tasks/_rules/telemetry_rules.py +121 -0
- higpertext/hooks/hook_tasks/audit_logger_hook.py +28 -0
- higpertext/hooks/hook_tasks/hook_bash_guard.py +101 -0
- higpertext/hooks/hook_tasks/hook_code_quality.py +48 -0
- higpertext/hooks/hook_tasks/hook_context_hint.py +46 -0
- higpertext/hooks/hook_tasks/hook_context_manager.py +44 -0
- higpertext/hooks/hook_tasks/hook_io.py +122 -0
- higpertext/hooks/hook_tasks/hook_loop_guard.py +182 -0
- higpertext/hooks/hook_tasks/hook_post_observer.py +54 -0
- higpertext/hooks/hook_tasks/hook_read_guard.py +85 -0
- higpertext/hooks/hook_tasks/hook_security_guard.py +81 -0
- higpertext/hooks/hook_tasks/hook_session_prompt.py +83 -0
- higpertext/hooks/hook_tasks/hook_session_stop.py +115 -0
- higpertext/hooks/hook_tasks/hook_utils.py +144 -0
- higpertext/hooks/hook_tasks/session_guard_hook.py +23 -0
- higpertext/hooks/hook_tasks/telemetry_utils.py +176 -0
- higpertext/hooks/hook_tasks/test_echo_hook.py +33 -0
- higpertext/hooks/hook_tasks/webhook_hook.py +54 -0
- higpertext/hooks/hook_tasks/workflow_runner_hook.py +49 -0
- higpertext/hooks/hooks_catalog.json +116 -0
- higpertext/kernel/__init__.py +63 -0
- higpertext/kernel/_compat.py +138 -0
- higpertext/kernel/app_config.py +117 -0
- higpertext/kernel/application/__init__.py +13 -0
- higpertext/kernel/application/agent_registry.py +102 -0
- higpertext/kernel/application/capability_manager.py +61 -0
- higpertext/kernel/application/commit_reporter.py +247 -0
- higpertext/kernel/application/context_builder.py +166 -0
- higpertext/kernel/application/context_engine.py +409 -0
- higpertext/kernel/application/engine.py +41 -0
- higpertext/kernel/application/env_runtime.py +174 -0
- higpertext/kernel/application/environment_manager.py +154 -0
- higpertext/kernel/application/governance.py +192 -0
- higpertext/kernel/application/hook_registry.py +102 -0
- higpertext/kernel/application/hook_renderer.py +720 -0
- higpertext/kernel/application/ports.py +49 -0
- higpertext/kernel/application/profile_learner.py +358 -0
- higpertext/kernel/application/profile_service.py +205 -0
- higpertext/kernel/application/profile_services.py +6 -0
- higpertext/kernel/application/profile_use_cases.py +93 -0
- higpertext/kernel/application/rag_service.py +75 -0
- higpertext/kernel/application/roadmap_reporter.py +178 -0
- higpertext/kernel/application/semantic_engine.py +258 -0
- higpertext/kernel/application/session_services.py +33 -0
- higpertext/kernel/application/skill_hook_compiler.py +85 -0
- higpertext/kernel/application/telemetry.py +326 -0
- higpertext/kernel/application/workflow_manager.py +176 -0
- higpertext/kernel/config_paths.py +66 -0
- higpertext/kernel/domain/__init__.py +12 -0
- higpertext/kernel/domain/agent_registry.py +23 -0
- higpertext/kernel/domain/commit_reporter.py +155 -0
- higpertext/kernel/domain/compilers.py +7 -0
- higpertext/kernel/domain/context_engine.py +319 -0
- higpertext/kernel/domain/entities.py +51 -0
- higpertext/kernel/domain/env_runtime.py +62 -0
- higpertext/kernel/domain/governance.py +198 -0
- higpertext/kernel/domain/hook_models.py +29 -0
- higpertext/kernel/domain/profile_learner.py +186 -0
- higpertext/kernel/domain/rag.py +70 -0
- higpertext/kernel/domain/repositories.py +8 -0
- higpertext/kernel/domain/roadmap_reporter.py +80 -0
- higpertext/kernel/domain/semantic_engine.py +107 -0
- higpertext/kernel/engine.py +42 -0
- higpertext/kernel/htx_resolver.py +69 -0
- higpertext/kernel/infrastructure/__init__.py +13 -0
- higpertext/kernel/infrastructure/agent_registry.py +40 -0
- higpertext/kernel/infrastructure/cache/capability_cache.py +319 -0
- higpertext/kernel/infrastructure/capability_helper.py +40 -0
- higpertext/kernel/infrastructure/cli/__init__.py +1 -0
- higpertext/kernel/infrastructure/cli/agent_commands.py +62 -0
- higpertext/kernel/infrastructure/cli/arguments.py +39 -0
- higpertext/kernel/infrastructure/cli/capability_command_builder.py +86 -0
- higpertext/kernel/infrastructure/cli/capability_task_service.py +234 -0
- higpertext/kernel/infrastructure/cli/cli_search.py +234 -0
- higpertext/kernel/infrastructure/cli/parameter_contracts.py +83 -0
- higpertext/kernel/infrastructure/cli/parser_builder.py +122 -0
- higpertext/kernel/infrastructure/cli/profile_commands.py +89 -0
- higpertext/kernel/infrastructure/cli/roadmap_commands.py +117 -0
- higpertext/kernel/infrastructure/cli/router.py +1110 -0
- higpertext/kernel/infrastructure/cli/session_commands.py +36 -0
- higpertext/kernel/infrastructure/cli/task_commands.py +23 -0
- higpertext/kernel/infrastructure/cli/task_result_reporter.py +56 -0
- higpertext/kernel/infrastructure/cli/workflow_commands.py +25 -0
- higpertext/kernel/infrastructure/compilers/__init__.py +3 -0
- higpertext/kernel/infrastructure/compilers/factory.py +27 -0
- higpertext/kernel/infrastructure/compilers/graph_compiler.py +20 -0
- higpertext/kernel/infrastructure/compilers/guide_compiler.py +50 -0
- higpertext/kernel/infrastructure/compilers/hook_compiler.py +69 -0
- higpertext/kernel/infrastructure/compilers/playbook_compiler.py +154 -0
- higpertext/kernel/infrastructure/context_engine.py +303 -0
- higpertext/kernel/infrastructure/database/local_vector_store.py +99 -0
- higpertext/kernel/infrastructure/deployment/__init__.py +1 -0
- higpertext/kernel/infrastructure/deployment/resource_deployer.py +283 -0
- higpertext/kernel/infrastructure/diagnostics/__init__.py +1 -0
- higpertext/kernel/infrastructure/diagnostics/health.py +191 -0
- higpertext/kernel/infrastructure/env_runtime.py +227 -0
- higpertext/kernel/infrastructure/execution/__init__.py +1 -0
- higpertext/kernel/infrastructure/execution/parallel.py +188 -0
- higpertext/kernel/infrastructure/execution/resilience.py +155 -0
- higpertext/kernel/infrastructure/file_repositories.py +213 -0
- higpertext/kernel/infrastructure/governance.py +198 -0
- higpertext/kernel/infrastructure/hook_config_loader.py +53 -0
- higpertext/kernel/infrastructure/hook_webhook_dispatcher.py +61 -0
- higpertext/kernel/infrastructure/hook_workflow_bridge.py +60 -0
- higpertext/kernel/infrastructure/llm/__init__.py +6 -0
- higpertext/kernel/infrastructure/llm/provider.py +46 -0
- higpertext/kernel/infrastructure/llm/providers/__init__.py +0 -0
- higpertext/kernel/infrastructure/llm/providers/anthropic_provider.py +94 -0
- higpertext/kernel/infrastructure/llm/providers/gemini_embeddings.py +74 -0
- higpertext/kernel/infrastructure/llm/providers/gemini_provider.py +101 -0
- higpertext/kernel/infrastructure/llm/providers/ollama_provider.py +110 -0
- higpertext/kernel/infrastructure/llm/providers/openai_provider.py +98 -0
- higpertext/kernel/infrastructure/llm/registry.py +81 -0
- higpertext/kernel/infrastructure/logger.py +303 -0
- higpertext/kernel/infrastructure/output_store.py +70 -0
- higpertext/kernel/infrastructure/parser/__init__.py +1 -0
- higpertext/kernel/infrastructure/parser/code_chunker.py +144 -0
- higpertext/kernel/infrastructure/parser/language/__init__.py +14 -0
- higpertext/kernel/infrastructure/parser/language/base.py +41 -0
- higpertext/kernel/infrastructure/parser/language/powershell_parser.py +35 -0
- higpertext/kernel/infrastructure/parser/language/python_parser.py +98 -0
- higpertext/kernel/infrastructure/parser/language/typescript_parser.py +91 -0
- higpertext/kernel/infrastructure/parser/semantic_graph.py +409 -0
- higpertext/kernel/infrastructure/presentation/__init__.py +1 -0
- higpertext/kernel/infrastructure/presentation/html_renderer.py +137 -0
- higpertext/kernel/infrastructure/presentation/markdown_renderer.py +84 -0
- higpertext/kernel/infrastructure/presentation/markdown_report_renderer.py +97 -0
- higpertext/kernel/infrastructure/profile_store.py +28 -0
- higpertext/kernel/infrastructure/semantic_engine.py +289 -0
- higpertext/kernel/infrastructure/telemetry_reporter.py +132 -0
- higpertext/kernel/infrastructure/validation/__init__.py +1 -0
- higpertext/kernel/infrastructure/validation/contract_validator.py +163 -0
- higpertext/kernel/pkg_resources.py +38 -0
- higpertext/kernel/session_manager.py +319 -0
- higpertext/templates/env/generic-shell.yaml +21 -0
- higpertext/templates/env/node-vitest.yaml +27 -0
- higpertext/templates/env/python-pytest.yaml +29 -0
- higpertext/templates/html/commit_body.html +20 -0
- higpertext/templates/html/commit_diff.html +4 -0
- higpertext/templates/html/commit_index.html +29 -0
- higpertext/templates/html/commit_layer.html +11 -0
- higpertext/templates/html/commit_shell.html +28 -0
- higpertext/templates/html/graph_visualize.html +86 -0
- higpertext/templates/html/roadmap_body.html +12 -0
- higpertext/templates/html/roadmap_phase.html +5 -0
- higpertext/templates/html/roadmap_shell.html +29 -0
- higpertext/templates/markdown/commit_report.md +18 -0
- higpertext/templates/markdown/efficiency_report.md +12 -0
- higpertext/templates/markdown/roadmap_report.md +25 -0
- higpertext/templates/skills/best-practices.md +7 -0
- higpertext/templates/skills/clean-code.md +8 -0
- higpertext/templates/skills/ddd-standards.md +7 -0
- higpertext/templates/skills/tdd-practices.md +7 -0
- higpertext/templates/subagents/architect.md +7 -0
- higpertext/templates/subagents/test-engineer.md +7 -0
- higpertext/templates/workflows/build.json +23 -0
- higpertext/templates/workflows/compact.json +21 -0
- higpertext/templates/workflows/plan.json +59 -0
- higpertext/templates/workflows/review.json +26 -0
- higpertext/templates/workflows/spec.json +27 -0
- higpertext_cli-0.8.0.dist-info/METADATA +35 -0
- higpertext_cli-0.8.0.dist-info/RECORD +335 -0
- higpertext_cli-0.8.0.dist-info/WHEEL +5 -0
- higpertext_cli-0.8.0.dist-info/entry_points.txt +2 -0
- higpertext_cli-0.8.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import argparse
|
|
3
|
+
import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
6
|
+
|
|
7
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
8
|
+
_log = get_logger()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class K8sAuditor:
|
|
12
|
+
def __init__(self, target_dir: Path):
|
|
13
|
+
self.target_dir = target_dir.resolve()
|
|
14
|
+
self.findings = []
|
|
15
|
+
|
|
16
|
+
def _should_skip_dir(self, root: str) -> bool:
|
|
17
|
+
return any(d in root for d in (".git", WORKSPACE_DIR_NAME, "venv", "__pycache__"))
|
|
18
|
+
|
|
19
|
+
def _scan_directory_files(self, yaml_extensions: set[str]) -> int:
|
|
20
|
+
files_scanned = 0
|
|
21
|
+
for root, _, files in os.walk(self.target_dir):
|
|
22
|
+
if self._should_skip_dir(root):
|
|
23
|
+
continue
|
|
24
|
+
for file in files:
|
|
25
|
+
file_path = Path(root) / file
|
|
26
|
+
if file_path.suffix in yaml_extensions:
|
|
27
|
+
self.audit_file(file_path)
|
|
28
|
+
files_scanned += 1
|
|
29
|
+
return files_scanned
|
|
30
|
+
|
|
31
|
+
def scan_files(self):
|
|
32
|
+
"""Recursivamente busca archivos YAML y los audita."""
|
|
33
|
+
yaml_extensions = {".yaml", ".yml"}
|
|
34
|
+
if self.target_dir.is_file():
|
|
35
|
+
if self.target_dir.suffix in yaml_extensions:
|
|
36
|
+
self.audit_file(self.target_dir)
|
|
37
|
+
return 1
|
|
38
|
+
return 0
|
|
39
|
+
return self._scan_directory_files(yaml_extensions)
|
|
40
|
+
|
|
41
|
+
def audit_file(self, file_path: Path):
|
|
42
|
+
"""Audita un manifiesto YAML buscando malas configuraciones de seguridad."""
|
|
43
|
+
try:
|
|
44
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
45
|
+
content = f.read()
|
|
46
|
+
|
|
47
|
+
# Separar documentos YAML si hay múltiples con ---
|
|
48
|
+
documents = content.split("---")
|
|
49
|
+
for doc_idx, doc in enumerate(documents, 1):
|
|
50
|
+
# Validar si tiene pinta de manifiesto K8s (debe tener apiVersion y kind)
|
|
51
|
+
if "apiVersion:" not in doc or "kind:" not in doc:
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
lines = doc.splitlines()
|
|
55
|
+
self._check_security_context(file_path, doc_idx, lines)
|
|
56
|
+
self._check_network_sharing(file_path, doc_idx, lines)
|
|
57
|
+
self._check_resource_limits(file_path, doc_idx, lines)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
_log.warning(f"[!] Error leyendo {file_path.name}: {e}")
|
|
60
|
+
|
|
61
|
+
def _check_security_context(self, file_path: Path, doc_idx: int, lines: list):
|
|
62
|
+
"""Audita el SecurityContext del Pod / Contenedores."""
|
|
63
|
+
has_run_as_non_root = False
|
|
64
|
+
has_read_only_root_fs = False
|
|
65
|
+
has_allow_privilege_escalation = False
|
|
66
|
+
|
|
67
|
+
in_security_context = False
|
|
68
|
+
|
|
69
|
+
for line_idx, line in enumerate(lines, 1):
|
|
70
|
+
clean_line = line.strip()
|
|
71
|
+
|
|
72
|
+
# Detectar si estamos en un bloque de securityContext
|
|
73
|
+
if clean_line.startswith("securityContext:"):
|
|
74
|
+
in_security_context = True
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
# Salir de securityContext si la identación es menor (aproximado por espacios)
|
|
78
|
+
# Para simplificar, analizamos a nivel de línea en todo el archivo
|
|
79
|
+
|
|
80
|
+
if "privileged: true" in clean_line:
|
|
81
|
+
self.findings.append(
|
|
82
|
+
{
|
|
83
|
+
"id": "K8S-SEC-01",
|
|
84
|
+
"title": "Contenedor Privilegiado Detectado",
|
|
85
|
+
"severity": "CRITICAL",
|
|
86
|
+
"category": "Privileged Mode",
|
|
87
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)}:L{line_idx} (Doc {doc_idx})", # noqa: E501
|
|
88
|
+
"description": "El contenedor se ejecuta en modo privilegiado, lo que le da acceso completo al host y anula los límites de aislamiento.", # noqa: E501
|
|
89
|
+
"remediation": "Establecer `privileged: false` o remover la clave `privileged` del securityContext.", # noqa: E501
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if "runAsNonRoot: true" in clean_line:
|
|
94
|
+
has_run_as_non_root = True
|
|
95
|
+
|
|
96
|
+
if "runAsUser: 0" in clean_line:
|
|
97
|
+
self.findings.append(
|
|
98
|
+
{
|
|
99
|
+
"id": "K8S-SEC-02",
|
|
100
|
+
"title": "Ejecución explícita como root (runAsUser: 0)",
|
|
101
|
+
"severity": "CRITICAL",
|
|
102
|
+
"category": "User Context",
|
|
103
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)}:L{line_idx} (Doc {doc_idx})", # noqa: E501
|
|
104
|
+
"description": "El contenedor está configurado explícitamente para ejecutarse con el usuario UID 0 (root).", # noqa: E501
|
|
105
|
+
"remediation": "Configurar un usuario no root en el Dockerfile o cambiar `runAsUser` a un UID superior a 1000.", # noqa: E501
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if "readOnlyRootFilesystem: true" in clean_line:
|
|
110
|
+
has_read_only_root_fs = True
|
|
111
|
+
|
|
112
|
+
if "allowPrivilegeEscalation: true" in clean_line:
|
|
113
|
+
self.findings.append(
|
|
114
|
+
{
|
|
115
|
+
"id": "K8S-SEC-03",
|
|
116
|
+
"title": "Permisión de Escalado de Privilegios (allowPrivilegeEscalation: true)", # noqa: E501
|
|
117
|
+
"severity": "HIGH",
|
|
118
|
+
"category": "Privilege Escalation",
|
|
119
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)}:L{line_idx} (Doc {doc_idx})", # noqa: E501
|
|
120
|
+
"description": "El proceso del contenedor puede adquirir más privilegios que su proceso padre.", # noqa: E501
|
|
121
|
+
"remediation": "Establecer `allowPrivilegeEscalation: false` en el securityContext de cada contenedor.", # noqa: E501
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Alertas por omisiones si es un Deployment o Pod
|
|
126
|
+
doc_str = "\n".join(lines)
|
|
127
|
+
if (
|
|
128
|
+
"kind: Pod" in doc_str
|
|
129
|
+
or "kind: Deployment" in doc_str
|
|
130
|
+
or "kind: StatefulSet" in doc_str
|
|
131
|
+
or "kind: DaemonSet" in doc_str
|
|
132
|
+
):
|
|
133
|
+
if not has_run_as_non_root and "runAsNonRoot: false" not in doc_str:
|
|
134
|
+
self.findings.append(
|
|
135
|
+
{
|
|
136
|
+
"id": "K8S-SEC-04",
|
|
137
|
+
"title": "Falta runAsNonRoot: true en el SecurityContext",
|
|
138
|
+
"severity": "HIGH",
|
|
139
|
+
"category": "User Context",
|
|
140
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)} (Doc {doc_idx})", # noqa: E501
|
|
141
|
+
"description": "El manifiesto no exige que el contenedor corra como un usuario no root.", # noqa: E501
|
|
142
|
+
"remediation": "Agregar `runAsNonRoot: true` dentro del `securityContext` a nivel de Pod o Contenedor.", # noqa: E501
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
if not has_read_only_root_fs:
|
|
146
|
+
self.findings.append(
|
|
147
|
+
{
|
|
148
|
+
"id": "K8S-SEC-05",
|
|
149
|
+
"title": "Sistema de archivos raíz mutable (Root FS no de solo lectura)",
|
|
150
|
+
"severity": "MEDIUM",
|
|
151
|
+
"category": "Immutable Infrastructure",
|
|
152
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)} (Doc {doc_idx})", # noqa: E501
|
|
153
|
+
"description": "El contenedor puede escribir en su sistema de archivos raíz, lo que facilita persistencia de malware ante un compromiso.", # noqa: E501
|
|
154
|
+
"remediation": "Agregar `readOnlyRootFilesystem: true` en el securityContext del contenedor y usar `emptyDir` o montajes específicos para directorios de escritura temporal.", # noqa: E501
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def _check_network_sharing(self, file_path: Path, doc_idx: int, lines: list):
|
|
159
|
+
"""Revisa si comparte namespaces de red, PID o IPC con el host."""
|
|
160
|
+
for line_idx, line in enumerate(lines, 1):
|
|
161
|
+
clean_line = line.strip()
|
|
162
|
+
if "hostNetwork: true" in clean_line:
|
|
163
|
+
self.findings.append(
|
|
164
|
+
{
|
|
165
|
+
"id": "K8S-NET-01",
|
|
166
|
+
"title": "Uso de hostNetwork de la máquina host",
|
|
167
|
+
"severity": "HIGH",
|
|
168
|
+
"category": "Host Namespace Sharing",
|
|
169
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)}:L{line_idx} (Doc {doc_idx})", # noqa: E501
|
|
170
|
+
"description": "El Pod comparte el espacio de nombres de red del host, permitiéndole sniffear tráfico de la máquina física.", # noqa: E501
|
|
171
|
+
"remediation": "Eliminar `hostNetwork: true` y usar servicios/ingresses de Kubernetes para exponer la aplicación.", # noqa: E501
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
if "hostPID: true" in clean_line:
|
|
175
|
+
self.findings.append(
|
|
176
|
+
{
|
|
177
|
+
"id": "K8S-NET-02",
|
|
178
|
+
"title": "Uso de hostPID compartido",
|
|
179
|
+
"severity": "CRITICAL",
|
|
180
|
+
"category": "Host Namespace Sharing",
|
|
181
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)}:L{line_idx} (Doc {doc_idx})", # noqa: E501
|
|
182
|
+
"description": "El Pod comparte los procesos del host físico, pudiendo ver e interactuar con otros procesos de la máquina virtual/física.", # noqa: E501
|
|
183
|
+
"remediation": "Remover `hostPID: true` del manifiesto.",
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
if "hostIPC: true" in clean_line:
|
|
187
|
+
self.findings.append(
|
|
188
|
+
{
|
|
189
|
+
"id": "K8S-NET-03",
|
|
190
|
+
"title": "Uso de hostIPC compartido",
|
|
191
|
+
"severity": "CRITICAL",
|
|
192
|
+
"category": "Host Namespace Sharing",
|
|
193
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)}:L{line_idx} (Doc {doc_idx})", # noqa: E501
|
|
194
|
+
"description": "El Pod comparte memoria IPC con el host, exponiendo comunicación inter-procesos compartida.", # noqa: E501
|
|
195
|
+
"remediation": "Remover `hostIPC: true` del manifiesto.",
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
def _check_resource_limits(self, file_path: Path, doc_idx: int, lines: list):
|
|
200
|
+
"""Verifica la definición de límites de recursos para prevenir DoS."""
|
|
201
|
+
doc_str = "\n".join(lines)
|
|
202
|
+
if (
|
|
203
|
+
"kind: Pod" in doc_str
|
|
204
|
+
or "kind: Deployment" in doc_str
|
|
205
|
+
or "kind: StatefulSet" in doc_str
|
|
206
|
+
or "kind: DaemonSet" in doc_str
|
|
207
|
+
):
|
|
208
|
+
# Si no hay especificaciones de límites
|
|
209
|
+
if "limits:" not in doc_str:
|
|
210
|
+
self.findings.append(
|
|
211
|
+
{
|
|
212
|
+
"id": "K8S-RES-01",
|
|
213
|
+
"title": "Sin límites de recursos configurados (Dos Risk)",
|
|
214
|
+
"severity": "LOW",
|
|
215
|
+
"category": "Resource Management",
|
|
216
|
+
"location": f"{file_path.relative_to(self.target_dir.parent)} (Doc {doc_idx})", # noqa: E501
|
|
217
|
+
"description": "Los contenedores no tienen límites definidos de CPU o Memoria. Un bug (ej. memory leak) puede consumir todos los recursos del nodo físico y tumbar otros pods.", # noqa: E501
|
|
218
|
+
"remediation": "Definir `resources.limits.cpu` y `resources.limits.memory` en la especificación del contenedor.", # noqa: E501
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def generate_report(self) -> str:
|
|
223
|
+
"""Genera un reporte final estructurado en Markdown."""
|
|
224
|
+
report = [
|
|
225
|
+
"# 🛡️ Reporte de Auditoría de Manifiestos Kubernetes (K8s Security)",
|
|
226
|
+
f"\n**Fecha del Análisis**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
|
227
|
+
f"**Ruta Evaluada**: `{self.target_dir}`",
|
|
228
|
+
f"**Total de Hallazgos**: **{len(self.findings)}**",
|
|
229
|
+
"\n---",
|
|
230
|
+
"\n## 📊 Resumen Ejecutivo",
|
|
231
|
+
"| ID | Severidad | Categoría | Título | Ubicación |",
|
|
232
|
+
"|---|---|---|---|---|",
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
# Ordenar por severidad
|
|
236
|
+
sev_map = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}
|
|
237
|
+
self.findings.sort(key=lambda x: sev_map.get(x["severity"], 4))
|
|
238
|
+
|
|
239
|
+
for f in self.findings:
|
|
240
|
+
sev_badge = (
|
|
241
|
+
f"**{f['severity']}**" if f["severity"] in {"CRITICAL", "HIGH"} else f["severity"]
|
|
242
|
+
)
|
|
243
|
+
report.append(f"| `{
|
|
244
|
+
f['id']}` | {sev_badge} | {
|
|
245
|
+
f['category']} | {
|
|
246
|
+
f['title']} | `{
|
|
247
|
+
f['location']}` |")
|
|
248
|
+
|
|
249
|
+
report.append("\n---")
|
|
250
|
+
report.append("\n## 🔍 Detalle de Hallazgos y Remediación")
|
|
251
|
+
|
|
252
|
+
if not self.findings:
|
|
253
|
+
report.append(
|
|
254
|
+
"\n> [!TIP]\n> **¡Excelente!** No se detectaron fallos de seguridad en los manifiestos YAML auditados." # noqa: E501
|
|
255
|
+
)
|
|
256
|
+
else:
|
|
257
|
+
for idx, f in enumerate(self.findings, 1):
|
|
258
|
+
report.append(f"\n### {idx}. [{f['severity']}] {f['title']} (`{f['id']}`)")
|
|
259
|
+
report.append(f"- **Categoría**: {f['category']}")
|
|
260
|
+
report.append(f"- **Ubicación**: `{f['location']}`")
|
|
261
|
+
report.append(f"- **Descripción**: {f['description']}")
|
|
262
|
+
report.append(f"\n#### 🛠️ Instrucciones de Remediación:")
|
|
263
|
+
report.append(f"> {f['remediation']}\n")
|
|
264
|
+
|
|
265
|
+
return "\n".join(report)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def main():
|
|
269
|
+
parser = argparse.ArgumentParser(description="Auditor de Manifiestos K8s de higpertext")
|
|
270
|
+
parser.add_argument("--target", default=".", help="Ruta al archivo o carpeta a escanear")
|
|
271
|
+
parser.add_argument(
|
|
272
|
+
"--save_report",
|
|
273
|
+
default="k8s_security_report.md",
|
|
274
|
+
help="Nombre del archivo de reporte",
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
args, unknown = parser.parse_known_args()
|
|
278
|
+
|
|
279
|
+
target_path = Path(args.target).resolve()
|
|
280
|
+
auditor = K8sAuditor(target_path)
|
|
281
|
+
|
|
282
|
+
files_scanned = auditor.scan_files()
|
|
283
|
+
report_md = auditor.generate_report()
|
|
284
|
+
|
|
285
|
+
# Imprimir en consola
|
|
286
|
+
_log.info(f"[*] Escaneo finalizado. Se analizaron {files_scanned} archivos YAML.")
|
|
287
|
+
_log.info(report_md)
|
|
288
|
+
|
|
289
|
+
# Guardar en archivo
|
|
290
|
+
reports_dir = target_path / WORKSPACE_DIR_NAME / "reportes"
|
|
291
|
+
if not reports_dir.exists():
|
|
292
|
+
# Fallback a carpeta local si es un archivo o directorio externo
|
|
293
|
+
reports_dir = Path.cwd() / WORKSPACE_DIR_NAME / "reportes"
|
|
294
|
+
|
|
295
|
+
reports_dir.mkdir(parents=True, exist_ok=True)
|
|
296
|
+
out_file = reports_dir / Path(args.save_report).name
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
with open(out_file, "w", encoding="utf-8") as f:
|
|
300
|
+
f.write(report_md)
|
|
301
|
+
_log.ok(f"\n[SUCCESS] Reporte guardado en: {out_file}")
|
|
302
|
+
except Exception as e:
|
|
303
|
+
_log.warning(f"[!] Error guardando reporte: {e}")
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
if __name__ == "__main__":
|
|
307
|
+
main()
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import argparse
|
|
4
|
+
import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
7
|
+
|
|
8
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
9
|
+
_log = get_logger()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SecretScanner:
|
|
13
|
+
def __init__(self, target_dir: Path):
|
|
14
|
+
self.target_dir = target_dir.resolve()
|
|
15
|
+
self.findings = []
|
|
16
|
+
|
|
17
|
+
# Patrones regex de detección de secretos
|
|
18
|
+
self.patterns = [
|
|
19
|
+
{
|
|
20
|
+
"id": "SEC-PAT-01",
|
|
21
|
+
"name": "Azure DevOps Personal Access Token (PAT)",
|
|
22
|
+
"regex": r"(?:^|[^a-zA-Z0-9])([a-z2-7][a-z0-9]{51})(?:$|[^a-zA-Z0-9])",
|
|
23
|
+
"severity": "CRITICAL",
|
|
24
|
+
"remediation": (
|
|
25
|
+
"Revocar el PAT inmediatamente en el portal de Azure DevOps "
|
|
26
|
+
"y moverlo a Azure Key Vault o variables de entorno."
|
|
27
|
+
),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "SEC-AWS-01",
|
|
31
|
+
"name": "AWS Access Key ID",
|
|
32
|
+
"regex": r"AKIA[0-9A-Z]{16}",
|
|
33
|
+
"severity": "CRITICAL",
|
|
34
|
+
"remediation": "Inhabilitar la clave en la consola de AWS IAM y rotarla de inmediato.", # noqa: E501
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "SEC-KEY-01",
|
|
38
|
+
"name": "Clave Privada PEM / SSH",
|
|
39
|
+
"regex": r"-----BEGIN [A-Z ]+ PRIVATE KEY-----",
|
|
40
|
+
"severity": "CRITICAL",
|
|
41
|
+
"remediation": (
|
|
42
|
+
"Mover la clave privada fuera del control de código fuente "
|
|
43
|
+
"y revocar cualquier acceso asociado."
|
|
44
|
+
),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "SEC-PWD-01",
|
|
48
|
+
"name": "Contraseña Hardcodeada Posible",
|
|
49
|
+
"regex": (
|
|
50
|
+
r"(?:password|passwd|pwd|db_pass|client_secret)\s*=\s*"
|
|
51
|
+
r"['\"]([^'\"]{4,})['\"]"
|
|
52
|
+
),
|
|
53
|
+
"severity": "HIGH",
|
|
54
|
+
"remediation": (
|
|
55
|
+
"Extraer la contraseña a un archivo de configuración "
|
|
56
|
+
"secreto (.env) o cargarlo a través de un proveedor de "
|
|
57
|
+
"secretos de infraestructura."
|
|
58
|
+
),
|
|
59
|
+
},
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
def scan_files(self) -> int:
|
|
63
|
+
files_scanned = 0
|
|
64
|
+
|
|
65
|
+
if self.target_dir.is_file():
|
|
66
|
+
self.audit_file(self.target_dir)
|
|
67
|
+
files_scanned += 1
|
|
68
|
+
else:
|
|
69
|
+
for root, _, files in os.walk(self.target_dir):
|
|
70
|
+
if (
|
|
71
|
+
".git" in root
|
|
72
|
+
or WORKSPACE_DIR_NAME in root
|
|
73
|
+
or "venv" in root
|
|
74
|
+
or "__pycache__" in root
|
|
75
|
+
or "node_modules" in root
|
|
76
|
+
):
|
|
77
|
+
continue
|
|
78
|
+
for file in files:
|
|
79
|
+
file_path = Path(root) / file
|
|
80
|
+
# Solo escanear archivos de texto legibles
|
|
81
|
+
valid_exts = {
|
|
82
|
+
".py",
|
|
83
|
+
".json",
|
|
84
|
+
".md",
|
|
85
|
+
".env",
|
|
86
|
+
".txt",
|
|
87
|
+
".ps1",
|
|
88
|
+
".yml",
|
|
89
|
+
".yaml",
|
|
90
|
+
".ini",
|
|
91
|
+
".con",
|
|
92
|
+
".cfg",
|
|
93
|
+
}
|
|
94
|
+
if file_path.suffix in valid_exts:
|
|
95
|
+
self.audit_file(file_path)
|
|
96
|
+
files_scanned += 1
|
|
97
|
+
|
|
98
|
+
return files_scanned
|
|
99
|
+
|
|
100
|
+
def audit_file(self, file_path: Path):
|
|
101
|
+
try:
|
|
102
|
+
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
103
|
+
lines = f.readlines()
|
|
104
|
+
except Exception:
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
for line_idx, line in enumerate(lines, 1):
|
|
108
|
+
# Saltar comentarios de documentación obvios
|
|
109
|
+
if line.strip().startswith("#") and "api_key" not in line.lower():
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
for p in self.patterns:
|
|
113
|
+
matches = re.findall(p["regex"], line)
|
|
114
|
+
if matches:
|
|
115
|
+
for m in matches:
|
|
116
|
+
# Si es una asignación de contraseña, la contraseña es el match group 1.
|
|
117
|
+
# De lo contrario es la coincidencia completa.
|
|
118
|
+
secret_value = m if isinstance(m, str) else m[0]
|
|
119
|
+
|
|
120
|
+
# Evitar detectar variables vacías o marcadores de posición
|
|
121
|
+
common_placeholders = {
|
|
122
|
+
"your_password",
|
|
123
|
+
"placeholder",
|
|
124
|
+
"admin",
|
|
125
|
+
"null",
|
|
126
|
+
"none",
|
|
127
|
+
"password",
|
|
128
|
+
}
|
|
129
|
+
if secret_value.strip().lower() in common_placeholders:
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
masked = self.mask_secret(secret_value)
|
|
133
|
+
|
|
134
|
+
self.findings.append(
|
|
135
|
+
{
|
|
136
|
+
"id": p["id"],
|
|
137
|
+
"title": p["name"],
|
|
138
|
+
"severity": p["severity"],
|
|
139
|
+
"location": (
|
|
140
|
+
f"{file_path.relative_to(self.target_dir.parent)}"
|
|
141
|
+
f":L{line_idx}"
|
|
142
|
+
),
|
|
143
|
+
"masked_value": masked,
|
|
144
|
+
"remediation": p["remediation"],
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def mask_secret(self, secret: str) -> str:
|
|
149
|
+
"""Enmascara el secreto dejando legibles solo los extremos."""
|
|
150
|
+
if len(secret) <= 6:
|
|
151
|
+
return "*****"
|
|
152
|
+
return f"{secret[:3]}*****{secret[-3:]}"
|
|
153
|
+
|
|
154
|
+
def generate_report(self) -> str:
|
|
155
|
+
report = [
|
|
156
|
+
"# 🛡️ Reporte de Auditoría de Credenciales y Secretos Expuestos",
|
|
157
|
+
f"\n**Fecha del Análisis**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
|
158
|
+
f"**Ruta Evaluada**: `{self.target_dir}`",
|
|
159
|
+
f"**Total de Hallazgos**: **{len(self.findings)}**",
|
|
160
|
+
"\n---",
|
|
161
|
+
"\n## 📊 Resumen Ejecutivo",
|
|
162
|
+
"| ID | Severidad | Credencial / Secreto Detectado | Ubicación | Valor Enmascarado |",
|
|
163
|
+
"|---|---|---|---|---|",
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
# Ordenar por criticidad
|
|
167
|
+
sev_map = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}
|
|
168
|
+
self.findings.sort(key=lambda x: sev_map.get(x["severity"], 4))
|
|
169
|
+
|
|
170
|
+
for f in self.findings:
|
|
171
|
+
sev_badge = (
|
|
172
|
+
f"**{f['severity']}**" if f["severity"] in {"CRITICAL", "HIGH"} else f["severity"]
|
|
173
|
+
)
|
|
174
|
+
report.append(f"| `{
|
|
175
|
+
f['id']}` | {sev_badge} | {
|
|
176
|
+
f['title']} | `{
|
|
177
|
+
f['location']}` | `{
|
|
178
|
+
f['masked_value']}` |")
|
|
179
|
+
|
|
180
|
+
report.append("\n---")
|
|
181
|
+
report.append("\n## 🔍 Detalle de Alertas y Acciones Correctivas")
|
|
182
|
+
|
|
183
|
+
if not self.findings:
|
|
184
|
+
report.append(
|
|
185
|
+
"\n> [!TIP]\n> **¡Excelente!** No se detectaron credenciales ni "
|
|
186
|
+
"secretos expuestos en la ruta auditada."
|
|
187
|
+
)
|
|
188
|
+
else:
|
|
189
|
+
for idx, f in enumerate(self.findings, 1):
|
|
190
|
+
report.append(f"\n### {idx}. [{f['severity']}] {f['title']} (`{f['id']}`)")
|
|
191
|
+
report.append(f"- **Ubicación**: `{f['location']}`")
|
|
192
|
+
report.append(f"- **Valor Enmascarado**: `{f['masked_value']}`")
|
|
193
|
+
report.append("\n#### 🛠️ Guía de Remediación:")
|
|
194
|
+
report.append(f"> {f['remediation']}\n")
|
|
195
|
+
|
|
196
|
+
return "\n".join(report)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def main():
|
|
200
|
+
parser = argparse.ArgumentParser(description="higpertext Security Credential Scanner")
|
|
201
|
+
parser.add_argument(
|
|
202
|
+
"--target_path", default=".", help="Ruta al archivo o directorio a escanear"
|
|
203
|
+
)
|
|
204
|
+
parser.add_argument(
|
|
205
|
+
"--output_report",
|
|
206
|
+
default="secret_scan_report.md",
|
|
207
|
+
help="Nombre del reporte de salida",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
args, unknown = parser.parse_known_args()
|
|
211
|
+
|
|
212
|
+
target_path = Path(args.target_path).resolve()
|
|
213
|
+
scanner = SecretScanner(target_path)
|
|
214
|
+
|
|
215
|
+
files_scanned = scanner.scan_files()
|
|
216
|
+
report_md = scanner.generate_report()
|
|
217
|
+
|
|
218
|
+
_log.info(f"[*] Escaneo de secretos completado. Se escanearon {files_scanned} archivos.")
|
|
219
|
+
_log.info(report_md)
|
|
220
|
+
|
|
221
|
+
# Guardar reporte
|
|
222
|
+
reports_dir = Path.cwd() / WORKSPACE_DIR_NAME / "reportes"
|
|
223
|
+
reports_dir.mkdir(parents=True, exist_ok=True)
|
|
224
|
+
out_file = reports_dir / Path(args.output_report).name
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
with open(out_file, "w", encoding="utf-8") as f:
|
|
228
|
+
f.write(report_md)
|
|
229
|
+
_log.ok(f"\n[SUCCESS] Reporte de secretos guardado en: {out_file}")
|
|
230
|
+
except Exception as e:
|
|
231
|
+
_log.warning(f"[!] Error escribiendo reporte: {e}")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
if __name__ == "__main__":
|
|
235
|
+
main()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "security.secret-scanner",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"name": "Escáner de Secretos y Credenciales (Secret Scanner)",
|
|
5
|
+
"description": "Escanea el código fuente e historial en busca de claves API expuestas, PATs, tokens y contraseñas.",
|
|
6
|
+
"entrypoint": "capabilities/security/scripts/secret_scanner.py",
|
|
7
|
+
"language": "python",
|
|
8
|
+
"parameters": [
|
|
9
|
+
{
|
|
10
|
+
"name": "target_path",
|
|
11
|
+
"type": "string",
|
|
12
|
+
"required": false,
|
|
13
|
+
"default": ".",
|
|
14
|
+
"description": "Directorio o archivo a escanear en busca de secretos."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "output_report",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"required": false,
|
|
20
|
+
"default": "secret_scan_report.md",
|
|
21
|
+
"description": "Nombre del archivo de reporte Markdown resultante."
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"contract": {
|
|
25
|
+
"format": "Markdown Secret Scan Report",
|
|
26
|
+
"rules": [
|
|
27
|
+
"Debe buscar claves API, contraseñas, PATs de Azure DevOps, tokens de AWS y claves privadas.",
|
|
28
|
+
"Debe enmascarar los secretos encontrados en el reporte de salida para evitar filtraciones secundarias.",
|
|
29
|
+
"Debe clasificar los hallazgos según la criticidad del secreto."
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""higpertext Hooks — sistema nativo de hooks, tasks y webhooks.
|
|
2
|
+
|
|
3
|
+
Re-exporta la API pública para mantener compatibilidad con imports externos
|
|
4
|
+
del estilo `from higpertext.hooks.X import Y` sin cambios en los consumidores.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# domain
|
|
8
|
+
from higpertext.kernel.domain.hook_models import HookDefinition, WebhookEvent # noqa: F401
|
|
9
|
+
|
|
10
|
+
# application
|
|
11
|
+
from higpertext.kernel.application.hook_registry import HookRegistry # noqa: F401
|
|
12
|
+
from higpertext.kernel.application.hook_renderer import HookRenderer # noqa: F401
|
|
13
|
+
from higpertext.kernel.application.skill_hook_compiler import ( # noqa: F401
|
|
14
|
+
SkillHookCompiler,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# infrastructure
|
|
18
|
+
from higpertext.kernel.infrastructure.hook_config_loader import ( # noqa: F401
|
|
19
|
+
load_hooks_config,
|
|
20
|
+
save_hooks_config,
|
|
21
|
+
)
|
|
22
|
+
from higpertext.kernel.infrastructure.hook_webhook_dispatcher import ( # noqa: F401
|
|
23
|
+
WebhookDispatcher,
|
|
24
|
+
)
|
|
25
|
+
from higpertext.kernel.infrastructure.hook_workflow_bridge import ( # noqa: F401
|
|
26
|
+
WorkflowHookBridge,
|
|
27
|
+
WorkflowHookEntry,
|
|
28
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Single shim — all hooks re-exports from infrastructure, application, and domain layers."""
|
|
2
|
+
|
|
3
|
+
# Domain
|
|
4
|
+
from higpertext.kernel.domain.hook_models import ( # noqa: F401
|
|
5
|
+
HookDefinition,
|
|
6
|
+
WebhookEvent,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
# Application
|
|
10
|
+
from higpertext.kernel.application.hook_registry import HookRegistry # noqa: F401
|
|
11
|
+
from higpertext.kernel.application.hook_renderer import HookRenderer # noqa: F401
|
|
12
|
+
from higpertext.kernel.application.skill_hook_compiler import ( # noqa: F401
|
|
13
|
+
SkillHookCompiler,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Infrastructure
|
|
17
|
+
from higpertext.kernel.infrastructure.hook_config_loader import ( # noqa: F401
|
|
18
|
+
load_hooks_config,
|
|
19
|
+
save_hooks_config,
|
|
20
|
+
)
|
|
21
|
+
from higpertext.kernel.infrastructure.hook_webhook_dispatcher import ( # noqa: F401
|
|
22
|
+
WebhookDispatcher,
|
|
23
|
+
)
|
|
24
|
+
from higpertext.kernel.infrastructure.hook_workflow_bridge import ( # noqa: F401
|
|
25
|
+
WorkflowHookBridge,
|
|
26
|
+
WorkflowHookEntry,
|
|
27
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Hook tasks autónomas — interfaz stdin/stdout JSON."""
|
|
File without changes
|