Files
antigravity-skills-reference/skills/skill-sentinel/scripts/analyzers/security.py
ProgramadorBrasil 61ec71c5c7 feat: add 52 specialized AI agent skills (#217)
New skills covering 10 categories:

**Security & Audit**: 007 (STRIDE/PASTA/OWASP), cred-omega (secrets management)
**AI Personas**: Karpathy, Hinton, Sutskever, LeCun (4 sub-skills), Altman, Musk, Gates, Jobs, Buffett
**Multi-agent Orchestration**: agent-orchestrator, task-intelligence, multi-advisor
**Code Analysis**: matematico-tao (Terence Tao-inspired mathematical code analysis)
**Social & Messaging**: Instagram Graph API, Telegram Bot, WhatsApp Cloud API, social-orchestrator
**Image Generation**: AI Studio (Gemini), Stability AI, ComfyUI Gateway, image-studio router
**Brazilian Domain**: 6 auction specialist modules, 2 legal advisors, auctioneers data scraper
**Product & Growth**: design, invention, monetization, analytics, growth engine
**DevOps & LLM Ops**: Docker/CI-CD/AWS, RAG/embeddings/fine-tuning
**Skill Governance**: installer, sentinel auditor, context management

Each skill includes:
- Standardized YAML frontmatter (name, description, risk, source, tags, tools)
- Structured sections (Overview, When to Use, How it Works, Best Practices)
- Python scripts and reference documentation where applicable
- Cross-platform compatibility (Claude Code, Antigravity, Cursor, Gemini CLI, Codex CLI)

Co-authored-by: ProgramadorBrasil <214873561+ProgramadorBrasil@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:04:07 +01:00

190 lines
7.5 KiB
Python

"""
Analyzer de seguranca.
Verifica: secrets hardcoded, SQL injection, validacao de input,
HTTPS enforcement, tokens em logs, padroes de autenticacao.
"""
from __future__ import annotations
import re
from pathlib import Path
from typing import Any, Dict, List, Tuple
from config import SECRET_EXCEPTIONS, SECRET_PATTERNS, SQL_INJECTION_PATTERNS
def _check_secrets(source: str, rel_path: str, skill_name: str) -> List[Dict[str, Any]]:
"""Verifica patterns de secrets hardcoded."""
findings = []
for i, line in enumerate(source.splitlines(), 1):
for pattern in SECRET_PATTERNS:
if pattern.search(line):
# Checar se eh excecao conhecida
is_exception = any(exc in line for exc in SECRET_EXCEPTIONS)
if is_exception:
continue
findings.append({
"skill_name": skill_name,
"dimension": "security",
"severity": "critical",
"category": "hardcoded_secret",
"title": f"Possivel secret hardcoded em {rel_path}:{i}",
"description": "Credencial ou token encontrado no codigo-fonte. "
"Mover para variavel de ambiente.",
"file_path": rel_path,
"line_number": i,
"recommendation": "Usar os.environ.get() ou arquivo .env (nao versionado)",
"effort": "low",
"impact": "high",
})
return findings
def _check_sql_injection(source: str, rel_path: str, skill_name: str) -> List[Dict[str, Any]]:
"""Verifica uso de f-strings/format em queries SQL."""
findings = []
for i, line in enumerate(source.splitlines(), 1):
for pattern in SQL_INJECTION_PATTERNS:
if pattern.search(line):
findings.append({
"skill_name": skill_name,
"dimension": "security",
"severity": "high",
"category": "sql_injection",
"title": f"Possivel SQL injection em {rel_path}:{i}",
"description": "Interpolacao de string em query SQL. Usar queries parametrizadas (?)",
"file_path": rel_path,
"line_number": i,
"recommendation": "Substituir f-string/format por query parametrizada: cursor.execute(sql, [param])",
"effort": "low",
"impact": "high",
})
return findings
def _check_https(source: str, rel_path: str, skill_name: str) -> List[Dict[str, Any]]:
"""Verifica se URLs usam HTTP em vez de HTTPS."""
findings = []
http_pattern = re.compile(r'["\']http://(?!localhost|127\.0\.0\.1|0\.0\.0\.0)')
for i, line in enumerate(source.splitlines(), 1):
if http_pattern.search(line):
findings.append({
"skill_name": skill_name,
"dimension": "security",
"severity": "medium",
"category": "insecure_http",
"title": f"URL HTTP insegura em {rel_path}:{i}",
"description": "Uso de HTTP em vez de HTTPS para comunicacao externa.",
"file_path": rel_path,
"line_number": i,
"recommendation": "Trocar http:// por https://",
"effort": "low",
"impact": "medium",
})
return findings
def _check_token_in_logs(source: str, rel_path: str, skill_name: str) -> List[Dict[str, Any]]:
"""Verifica se tokens/secrets aparecem em print/logging."""
findings = []
log_pattern = re.compile(
r'(?:print|logging\.\w+|logger\.\w+)\s*\(.*(?:token|secret|password|key|credential)',
re.I
)
for i, line in enumerate(source.splitlines(), 1):
if log_pattern.search(line):
findings.append({
"skill_name": skill_name,
"dimension": "security",
"severity": "high",
"category": "token_in_log",
"title": f"Possivel token em log em {rel_path}:{i}",
"description": "Dados sensiveis podem estar sendo logados.",
"file_path": rel_path,
"line_number": i,
"recommendation": "Nao logar tokens/secrets. Usar mascaramento ou remover do log.",
"effort": "low",
"impact": "high",
})
return findings
def _check_input_validation(source: str, rel_path: str, skill_name: str) -> List[Dict[str, Any]]:
"""Verifica se argumentos CLI sao validados."""
findings = []
# Se usa argparse mas nao tem choices/type/nargs restritivos, e warning leve
if "argparse" in source and "add_argument" in source:
# Verificar se ao menos alguns args tem type= ou choices=
args_count = source.count("add_argument")
typed_count = len(re.findall(r'add_argument\([^)]*(?:type=|choices=)', source))
if args_count > 3 and typed_count == 0:
findings.append({
"skill_name": skill_name,
"dimension": "security",
"severity": "low",
"category": "weak_input_validation",
"title": f"Validacao fraca de argumentos CLI em {rel_path}",
"description": f"{args_count} argumentos sem type= ou choices=",
"file_path": rel_path,
"recommendation": "Adicionar type= e choices= nos argumentos do argparse",
"effort": "low",
"impact": "low",
})
return findings
def analyze(skill_data: Dict[str, Any]) -> Tuple[float, List[Dict[str, Any]]]:
"""Analisa seguranca de uma skill. Retorna (score, findings)."""
score = 100.0
findings: List[Dict[str, Any]] = []
skill_name = skill_data["name"]
skill_path = Path(skill_data["path"])
has_auth_module = False
uses_env_vars = False
for rel_path in skill_data.get("python_files", []):
filepath = skill_path / rel_path
if not filepath.exists():
continue
try:
source = filepath.read_text(encoding="utf-8", errors="replace")
except OSError:
continue
if "auth" in rel_path.lower():
has_auth_module = True
if "os.environ" in source or "os.getenv" in source or "dotenv" in source:
uses_env_vars = True
# Checks
secret_findings = _check_secrets(source, rel_path, skill_name)
findings.extend(secret_findings)
score -= len([f for f in secret_findings if f["severity"] == "critical"]) * 20
score -= len([f for f in secret_findings if f["severity"] == "high"]) * 10
sql_findings = _check_sql_injection(source, rel_path, skill_name)
findings.extend(sql_findings)
score -= len(sql_findings) * 15
https_findings = _check_https(source, rel_path, skill_name)
findings.extend(https_findings)
score -= len(https_findings) * 5
token_findings = _check_token_in_logs(source, rel_path, skill_name)
findings.extend(token_findings)
score -= len(token_findings) * 10
input_findings = _check_input_validation(source, rel_path, skill_name)
findings.extend(input_findings)
score -= len(input_findings) * 2
# Bonus por boas praticas
if has_auth_module:
score = min(100.0, score + 5)
if uses_env_vars:
score = min(100.0, score + 5)
return max(0.0, min(100.0, score)), findings