diff --git a/.github/workflows/weekly-security-audit.yml b/.github/workflows/weekly-security-audit.yml new file mode 100644 index 00000000..c7f9a775 --- /dev/null +++ b/.github/workflows/weekly-security-audit.yml @@ -0,0 +1,112 @@ +name: Weekly Security Audit + +on: + schedule: + - cron: "0 8 * * 1" # Monday 8:00 UTC + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + dependency-confusion-check: + name: Dependency Confusion Scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.11" + + - name: Scan for unregistered pip install targets + id: dep-check + run: | + # Find all pip install commands and check package names + python scripts/check_dependency_confusion.py \ + $(find . -name "*.md" -o -name "*.py" -o -name "*.ts" -o -name "*.txt" -o -name "*.yaml" -o -name "*.svg" -o -name "*.ipynb" \ + | grep -v node_modules | grep -v .git | grep -v __pycache__ | grep -v .venv) \ + > dep-confusion-report.txt 2>&1 || true + if [ -s dep-confusion-report.txt ]; then + echo "has-findings=true" >> "$GITHUB_OUTPUT" + echo "### ⚠️ Dependency Confusion Findings" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + cat dep-confusion-report.txt >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + else + echo "has-findings=false" >> "$GITHUB_OUTPUT" + echo "### ✅ No dependency confusion findings" >> "$GITHUB_STEP_SUMMARY" + fi + + security-skills-scan: + name: Security Skills Scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.11" + + - name: Install dependencies + run: pip install pyyaml + + - name: Run security skills scan + continue-on-error: true + run: | + python scripts/security_scan.py packages/ \ + --exclude-tests \ + --min-severity high \ + --format text | tee security-report.txt + + - name: Generate JSON report + if: always() + run: | + python scripts/security_scan.py packages/ \ + --exclude-tests \ + --format json > weekly-security-report.json || true + + - name: Upload reports + if: always() + uses: actions/upload-artifact@ea165f8d65b6db9a6b7e75b195db6a6b2be22da8 # v4.6.2 + with: + name: weekly-security-audit + path: | + weekly-security-report.json + dep-confusion-report.txt + retention-days: 90 + + weak-crypto-check: + name: Weak Cryptography Scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Check for MD5/SHA1 in non-test code + run: | + echo "### Weak Cryptography Check" >> "$GITHUB_STEP_SUMMARY" + FINDINGS=$(grep -rn "hashlib\.md5\|hashlib\.sha1" --include="*.py" packages/ \ + | grep -v "test_" | grep -v "text_tool" | grep -v "security_skills" \ + | grep -v "example" | grep -v "benchmark" | grep -v "red_team" || true) + if [ -n "$FINDINGS" ]; then + echo "⚠️ MD5/SHA1 found in production code:" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "$FINDINGS" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + else + echo "✅ No weak cryptography in production code" >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Check for pickle in non-test code + run: | + FINDINGS=$(grep -rn "pickle\.load" --include="*.py" packages/ \ + | grep -v "test_" | grep -v "security_skills" | grep -v "# " || true) + if [ -n "$FINDINGS" ]; then + echo "⚠️ pickle usage found:" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "$FINDINGS" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + else + echo "✅ No pickle deserialization in production code" >> "$GITHUB_STEP_SUMMARY" + fi diff --git a/docs/integrations/openshell.md b/docs/integrations/openshell.md index 18a17e0d..63c348e8 100644 --- a/docs/integrations/openshell.md +++ b/docs/integrations/openshell.md @@ -73,7 +73,7 @@ Install the toolkit as an [OpenClaw skill](../packages/agentmesh-integrations/op ```bash # Inside the sandbox -pip install agentmesh +pip install agentmesh-platform # Use the skill scripts scripts/check-policy.sh --action "web_search" --tokens 1500 --policy policy.yaml diff --git a/packages/agent-compliance/docs/submissions/owasp-genai-implementation-guide.md b/packages/agent-compliance/docs/submissions/owasp-genai-implementation-guide.md index b7ca868e..408933a6 100644 --- a/packages/agent-compliance/docs/submissions/owasp-genai-implementation-guide.md +++ b/packages/agent-compliance/docs/submissions/owasp-genai-implementation-guide.md @@ -12,7 +12,7 @@ The stack consists of four components: | Component | Role | Install | |---|---|---| | **Agent OS** | Governance kernel — policy, sandbox, memory, MCP security | `pip install agent-os-kernel` | -| **AgentMesh** | Identity & trust — DIDs, SPIFFE, handshake, reputation | `pip install agentmesh` | +| **AgentMesh** | Identity & trust — DIDs, SPIFFE, handshake, reputation | `pip install agentmesh-platform` | | **Agent SRE** | Observability — SLOs, anomaly detection, chaos, OpenTelemetry | `pip install agent-sre` | | **Agent Runtime** | Runtime control — kill switch, execution rings, saga rollback | `pip install agent-runtime` | diff --git a/packages/agent-mesh/src/agentmesh/cli/main.py b/packages/agent-mesh/src/agentmesh/cli/main.py index 9f15795e..5af4e295 100644 --- a/packages/agent-mesh/src/agentmesh/cli/main.py +++ b/packages/agent-mesh/src/agentmesh/cli/main.py @@ -261,7 +261,7 @@ def register(agent_dir: str, name: str = None): try: from agentmesh.identity import AgentIdentity except ImportError: - console.print("[red]Error: agentmesh is not installed. Run: pip install agentmesh[/red]") + console.print("[red]Error: agentmesh is not installed. Run: pip install agentmesh-platform[/red]") return identity = AgentIdentity.create(agent_name) diff --git a/packages/agent-mesh/src/agentmesh/transport/grpc_transport.py b/packages/agent-mesh/src/agentmesh/transport/grpc_transport.py index 417d1e25..a0b7034f 100644 --- a/packages/agent-mesh/src/agentmesh/transport/grpc_transport.py +++ b/packages/agent-mesh/src/agentmesh/transport/grpc_transport.py @@ -8,7 +8,7 @@ Requires the ``grpcio`` and ``grpcio-tools`` libraries:: - pip install agentmesh[grpc] + pip install agentmesh-platform[grpc] """ from __future__ import annotations @@ -41,7 +41,7 @@ def _require_grpc() -> None: if not HAS_GRPC: raise ImportError( "The 'grpcio' package is required for gRPC transport. " - "Install it with: pip install agentmesh[grpc]" + "Install it with: pip install agentmesh-platform[grpc]" ) diff --git a/packages/agent-os/modules/control-plane/src/agent_control_plane/lifecycle.py b/packages/agent-os/modules/control-plane/src/agent_control_plane/lifecycle.py index cf6bcee2..746e5fd5 100644 --- a/packages/agent-os/modules/control-plane/src/agent_control_plane/lifecycle.py +++ b/packages/agent-os/modules/control-plane/src/agent_control_plane/lifecycle.py @@ -2427,11 +2427,11 @@ def _compute_version(self, module_name: str) -> str: module = sys.modules.get(module_name) if module and hasattr(module, '__file__') and module.__file__: with open(module.__file__, 'rb') as f: - return hashlib.md5(f.read()).hexdigest() + return hashlib.sha256(f.read()).hexdigest() except Exception as e: logger.warning(f"Could not compute version for {module_name}: {e}") - return hashlib.md5(module_name.encode()).hexdigest() + return hashlib.sha256(module_name.encode()).hexdigest() async def check_for_changes(self, agent_id: str) -> bool: """Check if an agent's code has changed""" diff --git a/packages/agent-os/modules/control-plane/src/agent_control_plane/ml_safety.py b/packages/agent-os/modules/control-plane/src/agent_control_plane/ml_safety.py index 0c55b896..41f8f303 100644 --- a/packages/agent-os/modules/control-plane/src/agent_control_plane/ml_safety.py +++ b/packages/agent-os/modules/control-plane/src/agent_control_plane/ml_safety.py @@ -82,7 +82,7 @@ def from_text(text: str) -> 'EmbeddingVector': """Create a simplified embedding from text""" # In production, would call actual embedding model # This is a simplified hash-based approach for demonstration - vector_hash = hashlib.md5(text.lower().encode()).hexdigest() + vector_hash = hashlib.sha256(text.lower().encode()).hexdigest() return EmbeddingVector(text=text, vector_hash=vector_hash) diff --git a/packages/agent-os/modules/mcp-kernel-server/src/mcp_kernel_server/tools.py b/packages/agent-os/modules/mcp-kernel-server/src/mcp_kernel_server/tools.py index 8a8ae8c9..6b3cdf38 100644 --- a/packages/agent-os/modules/mcp-kernel-server/src/mcp_kernel_server/tools.py +++ b/packages/agent-os/modules/mcp-kernel-server/src/mcp_kernel_server/tools.py @@ -335,7 +335,7 @@ async def _verify_claim(self, claim: str, context: str, threshold: float) -> dic # responses = [await call_model(m, claim) for m in models] # For demo: Generate deterministic mock responses - claim_hash = int(hashlib.md5(claim.encode()).hexdigest()[:8], 16) + claim_hash = int(hashlib.sha256(claim.encode()).hexdigest()[:8], 16) # Simulate model responses (in production, actual API calls) responses = [] diff --git a/packages/agent-os/modules/observability/README.md b/packages/agent-os/modules/observability/README.md index 6e472fc6..60e75774 100644 --- a/packages/agent-os/modules/observability/README.md +++ b/packages/agent-os/modules/observability/README.md @@ -20,7 +20,7 @@ This package provides metrics, tracing, and dashboards for monitoring Agent OS d ### Install Package ```bash -pip install agent-os-observability +pip install agent-os-kernel[observability] ``` ### Basic Usage diff --git a/packages/agentmesh-integrations/openclaw-skill/SKILL.md b/packages/agentmesh-integrations/openclaw-skill/SKILL.md index f089e898..73f0e1d0 100644 --- a/packages/agentmesh-integrations/openclaw-skill/SKILL.md +++ b/packages/agentmesh-integrations/openclaw-skill/SKILL.md @@ -30,7 +30,7 @@ score trust, and maintain tamper-evident audit logs — all from your agent's co Install the AgentMesh governance CLI: ```bash -pip install agentmesh-governance +pip install agent-governance-toolkit ``` > If `agentmesh-governance` is not yet on PyPI, install directly from source: diff --git a/scripts/check_dependency_confusion.py b/scripts/check_dependency_confusion.py new file mode 100644 index 00000000..59cd7b9f --- /dev/null +++ b/scripts/check_dependency_confusion.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +"""Pre-commit hook: detect unregistered PyPI package names in pip install commands. + +Scans staged files for `pip install ` where is not a known +registered package. Prevents dependency confusion attacks. + +Usage: + # Install as pre-commit hook + cp scripts/check_dependency_confusion.py .git/hooks/pre-commit + chmod +x .git/hooks/pre-commit + + # Or run manually + python scripts/check_dependency_confusion.py [files...] +""" + +import re +import subprocess +import sys + +# Known registered PyPI package names for this project +REGISTERED_PACKAGES = { + # Core packages (on PyPI) + "agent-os-kernel", + "agentmesh-platform", + "agent-hypervisor", + "agent-runtime", + "agent-sre", + "agent-governance-toolkit", + "agent-lightning", + "agent-marketplace", + # Common dependencies + "pydantic", "pyyaml", "cryptography", "pynacl", "httpx", "aiohttp", + "fastapi", "uvicorn", "structlog", "click", "rich", "numpy", "scipy", + "pytest", "pytest-asyncio", "pytest-cov", "ruff", "mypy", "build", + "openai", "anthropic", "langchain", "langchain-core", "crewai", + "redis", "sqlalchemy", "asyncpg", "chromadb", "pinecone-client", + "sentence-transformers", "prometheus-client", "opentelemetry-api", + "opentelemetry-sdk", "fhir.resources", "hl7apy", "zenpy", "freshdesk", + "google-adk", "safety", "jupyter", "vitest", "tsup", "typescript", + # With extras (base name is what matters) +} + +# Patterns that are always safe +SAFE_PATTERNS = { + "-e", "--editable", "-r", "--requirement", "--upgrade", "--no-cache-dir", + "--quiet", "--require-hashes", "--hash", ".", "..", "../..", +} + +PIP_INSTALL_RE = re.compile( + r'pip\s+install\s+(.+?)(?:\s*\\?\s*$|(?=\s*&&|\s*\||\s*;|\s*#))', + re.MULTILINE, +) + + +def extract_package_names(install_args: str) -> list[str]: + """Extract package names from a pip install argument string.""" + packages = [] + for token in install_args.split(): + # Skip flags + if token.startswith("-") or token in SAFE_PATTERNS: + continue + if token.startswith((".", "/", "\\", "http", "git+")): + continue + # Strip extras: package[extra] -> package + base = re.sub(r'\[.*\]', '', token) + # Strip version specifiers: package>=1.0 -> package + base = re.split(r'[><=!~]', base)[0] + # Strip markdown/quote artifacts + base = base.strip('`"\'(){}') + if base and base not in SAFE_PATTERNS: + packages.append(base) + return packages + + +def check_file(filepath: str) -> list[str]: + """Check a file for potentially unregistered pip install targets.""" + findings = [] + try: + with open(filepath, encoding="utf-8", errors="ignore") as f: + content = f.read() + except (OSError, UnicodeDecodeError): + return findings + + for match in PIP_INSTALL_RE.finditer(content): + line_num = content[:match.start()].count("\n") + 1 + packages = extract_package_names(match.group(1)) + for pkg in packages: + if pkg.lower() not in {p.lower() for p in REGISTERED_PACKAGES}: + findings.append( + f" {filepath}:{line_num}: " + f"'{pkg}' may not be registered on PyPI" + ) + return findings + + +def main() -> int: + # Get files to check + if len(sys.argv) > 1: + files = sys.argv[1:] + else: + # Pre-commit mode: check staged files + result = subprocess.run( + ["git", "diff", "--cached", "--name-only", "--diff-filter=ACM"], + capture_output=True, text=True, + ) + files = [ + f for f in result.stdout.strip().split("\n") + if f.endswith((".md", ".py", ".ts", ".txt", ".yaml", ".yml", ".ipynb", ".svg")) + ] + + all_findings = [] + for f in files: + all_findings.extend(check_file(f)) + + if all_findings: + print("⚠️ Potential dependency confusion detected:") + print() + for finding in all_findings: + print(finding) + print() + print("If the package IS registered on PyPI, add it to REGISTERED_PACKAGES") + print("in scripts/check_dependency_confusion.py") + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main())