diff --git a/src/agentready/assessors/security.py b/src/agentready/assessors/security.py index 2fb11209..ad0c27ef 100644 --- a/src/agentready/assessors/security.py +++ b/src/agentready/assessors/security.py @@ -187,11 +187,17 @@ def assess(self, repository: Repository) -> Finding: pkg = json.loads(package_json.read_text()) scripts = pkg.get("scripts", {}) - # Check for npm audit or yarn audit in scripts - if any("audit" in str(v) for v in scripts.values()): + # Check for npm/yarn/bun audit in scripts + audit_scripts = [ + str(v) for v in scripts.values() if "audit" in str(v) + ] + if audit_scripts: score += 10 tools_found.append("npm/yarn audit") - evidence.append("✓ npm/yarn audit configured") + if any("bun" in s for s in audit_scripts): + evidence.append("✓ bun audit configured") + else: + evidence.append("✓ npm/yarn audit configured") # Check for Snyk deps = { @@ -205,6 +211,21 @@ def assess(self, repository: Repository) -> Finding: except Exception: pass + # Check for dependency audit step in CI workflows (npm/yarn/bun/pnpm) + workflows_dir = repository.path / ".github" / "workflows" + if workflows_dir.exists(): + for wf in list(workflows_dir.glob("*.yml")) + list( + workflows_dir.glob("*.yaml") + ): + try: + if re.search(r"\b(?:bun|npm|yarn|pnpm)\s+audit\b", wf.read_text()): + score += 10 + tools_found.append("CI dependency audit") + evidence.append("✓ Dependency audit step in CI workflow") + break + except OSError: + continue + # 5. Secret detection in pre-commit (20 points) precommit_config = repository.path / ".pre-commit-config.yaml" if precommit_config.exists(): diff --git a/tests/unit/test_assessors_security.py b/tests/unit/test_assessors_security.py index 8fedfcba..f7587352 100644 --- a/tests/unit/test_assessors_security.py +++ b/tests/unit/test_assessors_security.py @@ -289,6 +289,46 @@ def test_javascript_security_tools(self, tmp_path): or "Snyk" in finding.measured_value ) + def test_bun_audit_in_ci_workflow(self, tmp_path): + """Test that bun audit in a CI workflow is detected.""" + # Initialize git repository + subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True) + + # Create a bare package.json (no audit script) and a workflow with bun audit + package_json = tmp_path / "package.json" + package_json.write_text('{"scripts": {"test": "vitest run"}}\n') + + workflows_dir = tmp_path / ".github" / "workflows" + workflows_dir.mkdir(parents=True) + (workflows_dir / "ci.yml").write_text( + "name: CI\n" + "on: [push, pull_request]\n" + "jobs:\n" + " quality:\n" + " runs-on: ubuntu-latest\n" + " steps:\n" + " - uses: oven-sh/setup-bun@v2\n" + " - run: bun install --frozen-lockfile\n" + " - run: bun audit\n" + ) + + repo = Repository( + path=tmp_path, + name="test-repo", + url=None, + branch="main", + commit_hash="abc123", + languages={"TypeScript": 100}, + total_files=10, + total_lines=100, + ) + + assessor = DependencySecurityAssessor() + finding = assessor.assess(repo) + + assert finding.score >= 10 + assert any("audit" in e for e in finding.evidence) + def test_renovate_json_configuration(self, tmp_path): """Test that Renovate configuration in renovate.json is detected.""" # Initialize git repository