From martinholovsky-claude-skills-generator
Implements tamper-evident audit logging, SIEM integration, vulnerability scanning, and compliance reporting for Python, Go, TypeScript apps.
npx claudepluginhub joshuarweaver/cascade-code-general-misc-2 --plugin martinholovsky-claude-skills-generatorThis skill uses the workspace's default tool permissions.
---
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
MANDATORY READING PROTOCOL: Before implementing audit logging, read
references/advanced-patterns.mdfor tamper-evident patterns andreferences/threat-model.mdfor log integrity attacks.
This skill provides security auditing and compliance capabilities:
Risk Level: HIGH
Justification:
Attack Surface:
| Component | Recommended | Purpose |
|---|---|---|
| Structured Logging | structlog (Python) | JSON log generation |
| Log Aggregation | Elasticsearch, Loki | Centralized storage |
| SIEM | Splunk, QRadar, Sentinel | Security monitoring |
| Integrity | Signed logs, WORM storage | Tamper evidence |
| Compliance | OpenSCAP, Prowler, Trivy | Assessment tools |
import hashlib
import hmac
import json
from datetime import datetime, timezone
class TamperEvidentLogger:
"""Audit logger with cryptographic integrity protection."""
def __init__(self, signing_key: bytes, output_path: str):
self._key = signing_key
self._path = output_path
self._sequence = 0
self._previous_hash = b'\x00' * 32
def log(self, event: str, actor: str = None, **context) -> dict:
"""Log a tamper-evident audit entry."""
self._sequence += 1
entry = {
'timestamp': datetime.now(timezone.utc).isoformat(),
'sequence': self._sequence,
'event': event,
'actor': actor,
'context': context,
'previous_hash': self._previous_hash.hex(),
}
# Calculate and sign
entry_bytes = json.dumps(entry, sort_keys=True).encode()
entry['hash'] = hashlib.sha256(entry_bytes).hexdigest()
entry['signature'] = hmac.new(
self._key, entry_bytes, hashlib.sha256
).hexdigest()
self._previous_hash = bytes.fromhex(entry['hash'])
with open(self._path, 'a') as f:
f.write(json.dumps(entry) + '\n')
return entry
๐ For complete implementation (verification, chain validation):
references/advanced-patterns.mdimport structlog
logger = structlog.get_logger()
class SecurityAuditLogger:
"""Security-focused audit logging."""
@staticmethod
def log_authentication(user_id: str, success: bool, method: str, ip: str):
"""Log authentication attempt."""
logger.info(
"auth.attempt",
user_id=user_id, # Never log email for privacy
success=success,
method=method,
ip_address=ip
)
@staticmethod
def log_authorization(user_id: str, resource: str, action: str, allowed: bool):
"""Log authorization decision."""
logger.info(
"authz.decision",
user_id=user_id,
resource=resource,
action=action,
allowed=allowed
)
@staticmethod
def log_data_access(user_id: str, resource_type: str, resource_id: str, action: str):
"""Log data access for compliance."""
logger.info(
"data.access",
user_id=user_id,
resource_type=resource_type,
resource_id=resource_id,
action=action
)
๐ For complete patterns (decorators, context managers, SIEM integration):
references/security-examples.mdclass SIEMForwarder:
def _to_cef(self, event: dict) -> str:
"""Convert event to CEF format for SIEM ingestion."""
severity = self._map_severity(event.get('level', 'INFO'))
return (f"CEF:0|JARVIS|SecurityAudit|1.0|{event.get('event', 'unknown')}|"
f"{event.get('event', 'Unknown Event')}|{severity}|"
f"src={event.get('ip_address', '')} suser={event.get('user_id', '')}")
๐ For full SIEM implementation: See references/security-examples.md#siem-integration
from dataclasses import dataclass
from typing import List
@dataclass
class Vulnerability:
id: str
severity: str
package: str
fixed_version: str
class VulnerabilityScanner:
def scan_dependencies(self, path: str) -> List[Vulnerability]:
"""Scan dependencies using pip-audit, trivy for containers."""
pass
๐ For complete scanner: See references/advanced-patterns.md#vulnerability-assessment
import pytest
from security_auditing import TamperEvidentLogger, SecurityAuditLogger
class TestTamperEvidentLogger:
def test_log_entry_contains_required_fields(self, tmp_path):
"""Each log entry must have timestamp, sequence, hash, signature."""
logger = TamperEvidentLogger(b'test-key', str(tmp_path / 'audit.log'))
entry = logger.log("user.login", actor="user123")
assert all(k in entry for k in ['timestamp', 'sequence', 'hash', 'signature'])
def test_chain_integrity_detects_tampering(self, tmp_path):
"""Tampered logs must be detected via chain validation."""
log_path = tmp_path / 'audit.log'
logger = TamperEvidentLogger(b'test-key', str(log_path))
logger.log("event1", actor="user1")
# Tamper with log file
tampered = log_path.read_text().replace('"event1"', '"TAMPERED"')
log_path.write_text(tampered)
valid, errors = logger.verify_chain()
assert not valid and len(errors) > 0
def test_no_pii_in_log_output(self, tmp_path):
"""PII patterns must not appear in logs."""
import re
log_path = tmp_path / 'audit.log'
logger = SecurityAuditLogger(str(log_path))
logger.log_authentication(user_id="user123", success=True, method="password", ip="192.168.1.1")
content = log_path.read_text()
assert not re.search(r'[\w\.-]+@[\w\.-]+', content) # No emails
# Implement only what's needed to pass the tests
class TamperEvidentLogger:
def __init__(self, signing_key: bytes, output_path: str):
self._key, self._path = signing_key, output_path
self._sequence, self._previous_hash = 0, b'\x00' * 32
def log(self, event: str, actor: str = None, **context) -> dict:
self._sequence += 1
entry = {'timestamp': datetime.now(timezone.utc).isoformat(),
'sequence': self._sequence, 'event': event, 'actor': actor}
# Add hash and signature...
return entry
After tests pass, refactor for better error handling, performance optimizations, and security hardening.
pytest tests/security_auditing/ -v --tb=short
pytest tests/security_auditing/ --cov=security_auditing --cov-report=term-missing
# BAD: Full scan every time
def scan_all_dependencies():
return subprocess.run(['pip-audit', '--format=json'], capture_output=True)
# GOOD: Incremental scan based on changes
class IncrementalScanner:
def scan_if_changed(self, requirements_path: str) -> List[Vulnerability]:
current_hash = self._hash_file(requirements_path)
if current_hash == self._last_hash:
return self._load_cached_results()
results = self._full_scan(requirements_path)
self._save_cache(current_hash, results)
return results
# BAD: No caching - fetch every time
def get_vulnerability_info(cve_id: str) -> dict:
return requests.get(f"https://nvd.nist.gov/vuln/detail/{cve_id}")
# GOOD: Cache with TTL
class VulnerabilityCache:
def get_vulnerability(self, cve_id: str) -> dict:
if cve_id in self._cache:
data, timestamp = self._cache[cve_id]
if datetime.now() - timestamp < self._ttl:
return data
data = self._fetch_from_api(cve_id)
self._cache[cve_id] = (data, datetime.now())
return data
# BAD: Sequential scanning
def scan_multiple_projects(paths: List[str]) -> List[Report]:
return [scan_project(path) for path in paths]
# GOOD: Parallel scanning with thread pool
from concurrent.futures import ThreadPoolExecutor, as_completed
def scan_multiple_projects_parallel(paths: List[str], max_workers: int = 4):
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(scan_project, p): p for p in paths}
return [f.result() for f in as_completed(futures)]
# BAD: Scan everything always
def full_security_audit(project_path: str):
scan_dependencies(project_path)
scan_secrets(project_path)
scan_code_vulnerabilities(project_path)
# GOOD: Targeted scans based on changes
def targeted_security_audit(project_path: str, changed_files: List[str]):
scans_needed = set()
for file in changed_files:
if file.endswith(('requirements.txt', 'package.json')):
scans_needed.add('dependencies')
elif file.endswith(('.env', '.yml', '.yaml')):
scans_needed.add('secrets')
elif file.endswith(('.py', '.js', '.ts')):
scans_needed.add('code')
# Only run needed scans
return {scan: globals()[f'scan_{scan}'](project_path) for scan in scans_needed}
# BAD: Unbounded resource usage
def scan_large_codebase(path: str):
for root, dirs, files in os.walk(path):
for file in files:
analyze_file(os.path.join(root, file))
# GOOD: Resource-bounded scanning
class BoundedScanner:
def __init__(self, max_memory_mb: int = 512, max_files: int = 10000):
self._max_memory = max_memory_mb * 1024 * 1024
self._max_files = max_files
def scan_with_limits(self, path: str):
import resource
resource.setrlimit(resource.RLIMIT_AS, (self._max_memory, -1))
files_scanned = 0
for root, _, files in os.walk(path):
for file in files:
if files_scanned >= self._max_files:
return
files_scanned += 1
analyze_file(os.path.join(root, file))
| CVE | Severity | Component | Mitigation |
|---|---|---|---|
| CVE-2023-50960 | Critical | QRadar | Command injection - Update QRadar |
| CVE-2023-50961 | High | QRadar | Stored XSS - Update QRadar |
| CVE-2023-2976 | Medium | Guava | File exposure - Update to 32.0+ |
| CVE-2024-22365 | Medium | PAM | DoS - Update Linux PAM |
| CVE-2023-22875 | Medium | QRadar | Info disclosure - Update |
| OWASP 2025 | Risk | Implementation |
|---|---|---|
| A09: Security Logging Failures | Critical | Tamper-evident logs, SIEM forwarding |
| A05: Security Misconfiguration | High | Log protection, retention policies |
| A01: Broken Access Control | High | Log access auditing |
๐ For detailed OWASP guidance:
references/security-examples.md#owasp-coveragedef test_log_integrity_tamper_detection(audit_logger):
"""Tampered logs must be detected."""
audit_logger.log("test.event", actor="user1")
# Tamper and verify detection
valid, errors = audit_logger.verify_chain()
assert not valid
def test_no_pii_in_logs(audit_logger):
"""PII must not appear in logs."""
# Check for email, phone, SSN patterns in log output
pass
๐ For complete test suite:
references/security-examples.md#testing# โ NEVER: Log passwords, tokens, PII
logger.info(f"User {email} logged in with password {password}")
# โ
ALWAYS: Log identifiers only
logger.info("user.login", user_id=user.id, method="password")
# โ NEVER: Plain text logs anyone can modify
with open('audit.log', 'a') as f:
f.write(json.dumps(event) + '\n')
# โ
ALWAYS: Signed, chained logs
entry['signature'] = hmac.new(key, data, hashlib.sha256).hexdigest()
# โ NEVER: Untraced requests
logger.info("Processing request")
# โ
ALWAYS: Include correlation ID
logger.info("request.processing", correlation_id=request.id)
# โ chmod 644 /var/log/audit.log # World-readable
# โ
chmod 600 /var/log/audit.log # Restricted
๐ For complete anti-patterns: See references/advanced-patterns.md#anti-patterns
references/advanced-patterns.md - Full implementations, WORM storagereferences/security-examples.md - SIEM configs, compliance reportsreferences/threat-model.md - Log integrity attack scenariosIf it's not logged, it didn't happen. If logs can be tampered, you can't prove anything.