codejury 0.10.1__tar.gz → 0.10.2__tar.gz
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.
- {codejury-0.10.1 → codejury-0.10.2}/PKG-INFO +13 -13
- {codejury-0.10.1 → codejury-0.10.2}/README.md +12 -12
- {codejury-0.10.1 → codejury-0.10.2}/codejury/__init__.py +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/agents/__init__.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/agents/debate.py +3 -3
- {codejury-0.10.1 → codejury-0.10.2}/codejury/agents/mock.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/agents/parsing.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/agents/refuter.py +3 -3
- {codejury-0.10.1 → codejury-0.10.2}/codejury/agents/verifier.py +4 -4
- {codejury-0.10.1 → codejury-0.10.2}/codejury/analysis/__init__.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/analysis/provenance.py +5 -5
- {codejury-0.10.1 → codejury-0.10.2}/codejury/analysis/taint.py +6 -6
- {codejury-0.10.1 → codejury-0.10.2}/codejury/assembly.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/baseline.py +9 -9
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/business_logic.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/dependency_config.yaml +3 -3
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/excessive_agency.yaml +3 -3
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/input_validation.yaml +6 -6
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/insecure_output_handling.yaml +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/prompt_injection.yaml +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/secrets.yaml +5 -5
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ag_fixed_enum_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ag_model_confirmed_vuln.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/authn_sha256_checksum_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/deserialize_json_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/deserialize_pickle_vuln.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ioh_json_response_safe.yaml +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ioh_output_to_sql_vuln.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/pi_user_content_concat_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/session_secure_cookie_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/sql_constant_concat_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ssrf_constant_url_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/xfile_idor_no_check_vuln.yaml +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/xfile_idor_owner_checked_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/xfile_path_sanitized_safe.yaml +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/xfile_path_tainted_vuln.yaml +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/xss_textcontent_safe.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/suppressions.yaml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/taint.yaml +4 -4
- {codejury-0.10.1 → codejury-0.10.2}/codejury/domain/__init__.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/domain/artifact.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/domain/capability.py +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/domain/context.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/domain/observation.py +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/domain/result.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/evaluation.py +6 -6
- codejury-0.10.2/codejury/infrastructure/__init__.py +4 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/infrastructure/cache.py +4 -4
- codejury-0.10.2/codejury/integrations/__init__.py +1 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/__init__.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/adaptive.py +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/base.py +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/challenge.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/debate.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/pipeline.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/reflexion.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/single.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/orchestrators/taint_gate.py +3 -3
- {codejury-0.10.1 → codejury-0.10.2}/codejury/providers/__init__.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/providers/anthropic.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/providers/litellm.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/providers/mock.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/providers/openai.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/providers/retry.py +5 -5
- {codejury-0.10.1 → codejury-0.10.2}/codejury/reporting.py +4 -4
- {codejury-0.10.1 → codejury-0.10.2}/codejury/sources/__init__.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/sources/callers.py +6 -6
- {codejury-0.10.1 → codejury-0.10.2}/codejury/sources/chunker.py +3 -3
- {codejury-0.10.1 → codejury-0.10.2}/codejury/sources/diff.py +6 -6
- {codejury-0.10.1 → codejury-0.10.2}/codejury/sources/function.py +5 -5
- {codejury-0.10.1 → codejury-0.10.2}/codejury/sources/mock.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/sources/repo.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/suppression.py +8 -8
- {codejury-0.10.1 → codejury-0.10.2}/codejury/tasks/__init__.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/codejury/tasks/base.py +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/codejury.egg-info/PKG-INFO +13 -13
- {codejury-0.10.1 → codejury-0.10.2}/pyproject.toml +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_cache.py +1 -1
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_taint.py +2 -2
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_taint_gate.py +2 -2
- codejury-0.10.1/codejury/infrastructure/__init__.py +0 -4
- codejury-0.10.1/codejury/integrations/__init__.py +0 -1
- {codejury-0.10.1 → codejury-0.10.2}/LICENSE +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/agents/base.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/cli.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/authentication.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/authorization.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/crypto.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/data_protection.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/error_logging.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/output_encoding.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/session.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ag_allowlist_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ag_arbitrary_tool_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ag_destructive_no_confirm_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ag_human_approval_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/authn_bcrypt_password.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/authn_jwt_noverify_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/authn_jwt_verified_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/authn_sha256_password.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/authn_weak_hash_indirect_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/authz_idor_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/authz_owner_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/business_logic_price_tamper_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/business_logic_server_checked_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/cmdi_fixed_argv_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/cmdi_ossystem_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/cmdi_subprocess_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/crypto_aesgcm_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/crypto_ecb_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/data_protection_plaintext_pii_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/data_protection_tokenized_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/dependency_config_tls_verify_off_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/dependency_config_tls_verify_on_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/error_logging_redacted_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/error_logging_secret_leak_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ioh_escaped_output_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ioh_exec_output_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ioh_innerhtml_output_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ioh_schema_validated_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/literal_eval_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/path_basename_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/path_contained_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/path_traversal_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/pi_delimited_data_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/pi_format_role_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/pi_indirect_rag_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/pi_system_concat_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/pi_user_role_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/secrets_env_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/secrets_hardcoded_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/session_fixation_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/sqli_format_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/sqli_fstring_query.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/sqli_indirect_var_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/sqli_parameterized_query.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ssrf_allowlist_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ssrf_substring_allowlist_bypass_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/ssrf_user_url_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/xss_innerhtml_constant_safe.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/golden/xss_innerhtml_vuln.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/tasks/audit_diff_debate.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/data/tasks/quick_scan_single.yaml +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/infrastructure/json_parse.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/integrations/github.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/providers/base.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/providers/openai_format.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/resources.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/sources/base.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury/tasks/registry.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury.egg-info/SOURCES.txt +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury.egg-info/dependency_links.txt +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury.egg-info/entry_points.txt +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury.egg-info/requires.txt +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/codejury.egg-info/top_level.txt +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/setup.cfg +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_adaptive.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_anthropic_provider.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_assembly.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_audit_pipeline.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_baseline.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_callers.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_capability.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_challenge.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_cli_audit.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_context.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_debate_agents.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_debate_orchestrator.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_diff_source.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_evaluation.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_function_source.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_integrations.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_json_parse.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_litellm_provider.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_openai_provider.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_orchestrator.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_parsing.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_pipeline_orchestrator.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_provenance.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_reflexion_orchestrator.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_repo_source.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_reporting.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_retry_provider.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_review_fixes.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_sarif.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_suppression.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_taint_crossfile.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_tasks.py +0 -0
- {codejury-0.10.1 → codejury-0.10.2}/tests/test_verifier.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codejury
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.2
|
|
4
4
|
Summary: General-purpose Application Security AI audit framework -- five-layer architecture, capabilities as first-class data
|
|
5
5
|
Author: AISecLabs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -32,22 +32,22 @@ Dynamic: license-file
|
|
|
32
32
|
|
|
33
33
|
An AI security auditor for code whose knowledge lives in versioned YAML, not in
|
|
34
34
|
prompts. It reviews a diff or a whole repository against the OWASP ASVS and
|
|
35
|
-
reports a verdict per dimension
|
|
35
|
+
reports a verdict per dimension: both what is **vulnerable** and what is
|
|
36
36
|
**verified safe**.
|
|
37
37
|
|
|
38
|
-
The name is the core idea: code goes before a "jury" of adversarial roles
|
|
39
|
-
Finder / Challenger / Judge
|
|
38
|
+
The name is the core idea: code goes before a "jury" of adversarial roles,
|
|
39
|
+
Finder / Challenger / Judge, that argue and converge on a verdict.
|
|
40
40
|
|
|
41
41
|
Why it is built this way:
|
|
42
42
|
|
|
43
43
|
- **Knowledge is data.** Each OWASP ASVS area (and now OWASP LLM Top 10 areas) is a YAML capability
|
|
44
|
-
(safe patterns + anti-patterns, with CWE and examples)
|
|
44
|
+
(safe patterns + anti-patterns, with CWE and examples), versioned, reviewable
|
|
45
45
|
in a PR, and editable by non-engineers. The framework core stays small.
|
|
46
46
|
- **Verdicts, not just alerts.** Every capability yields `SECURE` / `VULNERABLE`
|
|
47
47
|
/ `PARTIAL` / `NOT_PRESENT`, so a report shows what was checked and *passed*,
|
|
48
48
|
not only what failed.
|
|
49
49
|
- **Composable.** Four orchestration strategies, four model backends, and
|
|
50
|
-
diff / repo inputs are chosen per run
|
|
50
|
+
diff / repo inputs are chosen per run; mix and match.
|
|
51
51
|
|
|
52
52
|
## Install
|
|
53
53
|
|
|
@@ -65,7 +65,7 @@ git diff origin/main... | codejury audit --fail-on high -
|
|
|
65
65
|
# Post inline review comments on a GitHub pull request (needs GITHUB_TOKEN)
|
|
66
66
|
git diff origin/main... | codejury audit --github your-org/your-repo#123 -
|
|
67
67
|
|
|
68
|
-
# No API key needed
|
|
68
|
+
# No API key needed: prove the pipeline runs end to end with mock layers
|
|
69
69
|
codejury dry-run
|
|
70
70
|
|
|
71
71
|
# A real audit: set a key, then review your staged changes
|
|
@@ -104,7 +104,7 @@ caller/callee context). It downgrades only on positive proof, so it removes fals
|
|
|
104
104
|
positives without dropping real findings.
|
|
105
105
|
|
|
106
106
|
`--orchestrator adaptive` keeps cost down: it runs the single verifier first and
|
|
107
|
-
escalates to a full debate only for artifacts worth it
|
|
107
|
+
escalates to a full debate only for artifacts worth it: any VULNERABLE verdict
|
|
108
108
|
(verify a flag before reporting) or a low-confidence PARTIAL/UNKNOWN one. Clean,
|
|
109
109
|
confident files pay a single call; only the rest pay for debate.
|
|
110
110
|
|
|
@@ -118,7 +118,7 @@ outside C/C++) are dropped by versioned rules in
|
|
|
118
118
|
|
|
119
119
|
`codejury eval` takes `--dataset <dir>` (golden YAML directory), `--split <name>`
|
|
120
120
|
(score only cases tagged with that `split:`, e.g. a held-out set), and
|
|
121
|
-
`--format {text,json}
|
|
121
|
+
`--format {text,json}`; the JSON report is a stable schema (overall plus
|
|
122
122
|
per-capability confusion matrix and precision / recall / F1).
|
|
123
123
|
|
|
124
124
|
Runs are deterministic: providers query at temperature 0, and `audit` / `scan`
|
|
@@ -139,7 +139,7 @@ codejury scan ./myrepo --only secrets,input_validation,crypto
|
|
|
139
139
|
## Configuration
|
|
140
140
|
|
|
141
141
|
Provider keys are read from the environment (codejury does **not** auto-load
|
|
142
|
-
`.env
|
|
142
|
+
`.env`; copy `.env.example` and `source` it):
|
|
143
143
|
|
|
144
144
|
| Variable | Used by |
|
|
145
145
|
|---|---|
|
|
@@ -180,7 +180,7 @@ what `--only` and a task's `capabilities:` accept:
|
|
|
180
180
|
`business_logic` · `dependency_config` · `prompt_injection` ·
|
|
181
181
|
`insecure_output_handling` · `excessive_agency`
|
|
182
182
|
|
|
183
|
-
To tune for your codebase, edit these files (add patterns / sharpen wording)
|
|
183
|
+
To tune for your codebase, edit these files (add patterns / sharpen wording);
|
|
184
184
|
no code change needed.
|
|
185
185
|
|
|
186
186
|
## Architecture
|
|
@@ -210,13 +210,13 @@ independently.
|
|
|
210
210
|
can't see whether a value is attacker-controlled. Mitigations that add context
|
|
211
211
|
but do not fully solve it: `scan --callers` (where this file's functions are
|
|
212
212
|
called) and `scan --callees` (the called code it delegates to, so a sink in
|
|
213
|
-
another file is visible)
|
|
213
|
+
another file is visible); pair them for both directions; `--orchestrator
|
|
214
214
|
challenge` (a recall-safe
|
|
215
215
|
refutation pass that drops only provably-safe flags); `--only` to scope; or
|
|
216
216
|
`--orchestrator debate`. Real taint precision still needs data-flow analysis,
|
|
217
217
|
not model skepticism.
|
|
218
218
|
- **`scan` cost scales as files x capabilities.** It is a periodic deep audit,
|
|
219
|
-
not a quick check
|
|
219
|
+
not a quick check; scope it with `--only`. Day to day, audit the diff.
|
|
220
220
|
|
|
221
221
|
## Development
|
|
222
222
|
|
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
An AI security auditor for code whose knowledge lives in versioned YAML, not in
|
|
4
4
|
prompts. It reviews a diff or a whole repository against the OWASP ASVS and
|
|
5
|
-
reports a verdict per dimension
|
|
5
|
+
reports a verdict per dimension: both what is **vulnerable** and what is
|
|
6
6
|
**verified safe**.
|
|
7
7
|
|
|
8
|
-
The name is the core idea: code goes before a "jury" of adversarial roles
|
|
9
|
-
Finder / Challenger / Judge
|
|
8
|
+
The name is the core idea: code goes before a "jury" of adversarial roles,
|
|
9
|
+
Finder / Challenger / Judge, that argue and converge on a verdict.
|
|
10
10
|
|
|
11
11
|
Why it is built this way:
|
|
12
12
|
|
|
13
13
|
- **Knowledge is data.** Each OWASP ASVS area (and now OWASP LLM Top 10 areas) is a YAML capability
|
|
14
|
-
(safe patterns + anti-patterns, with CWE and examples)
|
|
14
|
+
(safe patterns + anti-patterns, with CWE and examples), versioned, reviewable
|
|
15
15
|
in a PR, and editable by non-engineers. The framework core stays small.
|
|
16
16
|
- **Verdicts, not just alerts.** Every capability yields `SECURE` / `VULNERABLE`
|
|
17
17
|
/ `PARTIAL` / `NOT_PRESENT`, so a report shows what was checked and *passed*,
|
|
18
18
|
not only what failed.
|
|
19
19
|
- **Composable.** Four orchestration strategies, four model backends, and
|
|
20
|
-
diff / repo inputs are chosen per run
|
|
20
|
+
diff / repo inputs are chosen per run; mix and match.
|
|
21
21
|
|
|
22
22
|
## Install
|
|
23
23
|
|
|
@@ -35,7 +35,7 @@ git diff origin/main... | codejury audit --fail-on high -
|
|
|
35
35
|
# Post inline review comments on a GitHub pull request (needs GITHUB_TOKEN)
|
|
36
36
|
git diff origin/main... | codejury audit --github your-org/your-repo#123 -
|
|
37
37
|
|
|
38
|
-
# No API key needed
|
|
38
|
+
# No API key needed: prove the pipeline runs end to end with mock layers
|
|
39
39
|
codejury dry-run
|
|
40
40
|
|
|
41
41
|
# A real audit: set a key, then review your staged changes
|
|
@@ -74,7 +74,7 @@ caller/callee context). It downgrades only on positive proof, so it removes fals
|
|
|
74
74
|
positives without dropping real findings.
|
|
75
75
|
|
|
76
76
|
`--orchestrator adaptive` keeps cost down: it runs the single verifier first and
|
|
77
|
-
escalates to a full debate only for artifacts worth it
|
|
77
|
+
escalates to a full debate only for artifacts worth it: any VULNERABLE verdict
|
|
78
78
|
(verify a flag before reporting) or a low-confidence PARTIAL/UNKNOWN one. Clean,
|
|
79
79
|
confident files pay a single call; only the rest pay for debate.
|
|
80
80
|
|
|
@@ -88,7 +88,7 @@ outside C/C++) are dropped by versioned rules in
|
|
|
88
88
|
|
|
89
89
|
`codejury eval` takes `--dataset <dir>` (golden YAML directory), `--split <name>`
|
|
90
90
|
(score only cases tagged with that `split:`, e.g. a held-out set), and
|
|
91
|
-
`--format {text,json}
|
|
91
|
+
`--format {text,json}`; the JSON report is a stable schema (overall plus
|
|
92
92
|
per-capability confusion matrix and precision / recall / F1).
|
|
93
93
|
|
|
94
94
|
Runs are deterministic: providers query at temperature 0, and `audit` / `scan`
|
|
@@ -109,7 +109,7 @@ codejury scan ./myrepo --only secrets,input_validation,crypto
|
|
|
109
109
|
## Configuration
|
|
110
110
|
|
|
111
111
|
Provider keys are read from the environment (codejury does **not** auto-load
|
|
112
|
-
`.env
|
|
112
|
+
`.env`; copy `.env.example` and `source` it):
|
|
113
113
|
|
|
114
114
|
| Variable | Used by |
|
|
115
115
|
|---|---|
|
|
@@ -150,7 +150,7 @@ what `--only` and a task's `capabilities:` accept:
|
|
|
150
150
|
`business_logic` · `dependency_config` · `prompt_injection` ·
|
|
151
151
|
`insecure_output_handling` · `excessive_agency`
|
|
152
152
|
|
|
153
|
-
To tune for your codebase, edit these files (add patterns / sharpen wording)
|
|
153
|
+
To tune for your codebase, edit these files (add patterns / sharpen wording);
|
|
154
154
|
no code change needed.
|
|
155
155
|
|
|
156
156
|
## Architecture
|
|
@@ -180,13 +180,13 @@ independently.
|
|
|
180
180
|
can't see whether a value is attacker-controlled. Mitigations that add context
|
|
181
181
|
but do not fully solve it: `scan --callers` (where this file's functions are
|
|
182
182
|
called) and `scan --callees` (the called code it delegates to, so a sink in
|
|
183
|
-
another file is visible)
|
|
183
|
+
another file is visible); pair them for both directions; `--orchestrator
|
|
184
184
|
challenge` (a recall-safe
|
|
185
185
|
refutation pass that drops only provably-safe flags); `--only` to scope; or
|
|
186
186
|
`--orchestrator debate`. Real taint precision still needs data-flow analysis,
|
|
187
187
|
not model skepticism.
|
|
188
188
|
- **`scan` cost scales as files x capabilities.** It is a periodic deep audit,
|
|
189
|
-
not a quick check
|
|
189
|
+
not a quick check; scope it with `--only`. Day to day, audit the diff.
|
|
190
190
|
|
|
191
191
|
## Development
|
|
192
192
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""codejury
|
|
1
|
+
"""codejury: a general-purpose Application Security AI audit framework.
|
|
2
2
|
|
|
3
|
-
Five layers: Task
|
|
3
|
+
Five layers: Task, then Capability plus Orchestrator plus Source plus Agent, then Provider, then Infrastructure.
|
|
4
4
|
Domain knowledge lives in YAML capability files as a first-class citizen,
|
|
5
5
|
aligned with OWASP ASVS.
|
|
6
6
|
"""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""codejury.agents
|
|
1
|
+
"""codejury.agents: the audit roles (finder / challenger / judge / verifier).
|
|
2
2
|
|
|
3
3
|
An agent reads an AnalysisContext and emits observations. It talks to a model
|
|
4
4
|
only through a Provider, never a vendor SDK, so the role logic stays independent
|
|
@@ -35,8 +35,8 @@ _FINDING_SHAPE = (
|
|
|
35
35
|
|
|
36
36
|
_DEEP_LENS = (
|
|
37
37
|
"Look past surface patterns for the deepest flaw:\n"
|
|
38
|
-
"- Trust anchors: what does this code trust to authenticate or authorize
|
|
39
|
-
"signature, role, or caller
|
|
38
|
+
"- Trust anchors: what does this code trust to authenticate or authorize, such as a key, token, header, "
|
|
39
|
+
"signature, role, or caller, and who controls that value? If the attacker supplies what is used to "
|
|
40
40
|
"verify them (e.g. their own public key, an unconfigured key that disables verification), passing the "
|
|
41
41
|
"check proves nothing.\n"
|
|
42
42
|
"- Order of operations: is an external, irreversible, or privileged action performed before the local "
|
|
@@ -101,7 +101,7 @@ class ChallengerAgent(_DebateAgent):
|
|
|
101
101
|
def run(self, ctx: AnalysisContext) -> list[Observation]:
|
|
102
102
|
parts = [
|
|
103
103
|
"Challenge the findings below. For each one you believe is a false positive, write a rebuttal. "
|
|
104
|
-
"Add new_findings for any real issue that was missed
|
|
104
|
+
"Add new_findings for any real issue that was missed, especially a deeper flaw the finder "
|
|
105
105
|
"stopped short of.",
|
|
106
106
|
_DEEP_LENS,
|
|
107
107
|
_code(ctx.artifact),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""MockAgent
|
|
1
|
+
"""MockAgent: a minimal Agent for the dry-run and tests.
|
|
2
2
|
|
|
3
3
|
It really calls the provider (so the dry-run exercises the agent -> provider
|
|
4
4
|
path), then emits one Verdict per in-scope capability, parking the model's reply
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Shared coercion from loosely-typed model JSON into domain values.
|
|
2
2
|
|
|
3
3
|
Agents parse model output that may omit, mistype, or invent fields. These
|
|
4
|
-
helpers coerce defensively
|
|
4
|
+
helpers coerce defensively: they never raise on bad input, they fall back.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import math
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"""RefuterAgent
|
|
1
|
+
"""RefuterAgent: a skeptic that tries to dismiss flagged verdicts as false positives.
|
|
2
2
|
|
|
3
3
|
Used by the challenge orchestrator: the verifier flags issues, then the refuter
|
|
4
4
|
gets the code plus the VULNERABLE verdicts (via ``ctx.history``) and argues which
|
|
5
|
-
are false positives
|
|
5
|
+
are false positives, e.g. a value that is not actually attacker-controlled or a
|
|
6
6
|
sink that is not reachable. It returns a Concession per verdict it refutes.
|
|
7
7
|
|
|
8
8
|
This is the cheap, focused alternative to a full debate: only flagged verdicts
|
|
@@ -53,7 +53,7 @@ class RefuterAgent(Agent):
|
|
|
53
53
|
"is not attacker-controlled: a stored data field, or traced (here or in the call sites) to "
|
|
54
54
|
"a trusted, config, or operator-supplied source. If its origin is not shown or could "
|
|
55
55
|
"plausibly be external input, do NOT refute. For other issue types (hardcoded secrets, "
|
|
56
|
-
"weak crypto, ...), a literal value is often the vulnerability itself
|
|
56
|
+
"weak crypto, ...), a literal value is often the vulnerability itself; do NOT refute "
|
|
57
57
|
"those just because a value is constant.\n\n"
|
|
58
58
|
"Respond with a single JSON object exactly like:\n" + _JSON_SHAPE
|
|
59
59
|
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"""VerifierAgent
|
|
1
|
+
"""VerifierAgent: check code against a capability's correct/anti patterns.
|
|
2
2
|
|
|
3
3
|
It renders the capability into a prompt, calls the provider once per capability,
|
|
4
4
|
and parses the JSON reply into Verdicts. It asks for one verdict per
|
|
5
5
|
sub_capability including SECURE / NOT_PRESENT, so a report can say what was
|
|
6
|
-
checked and what passed
|
|
6
|
+
checked and what passed, not only what failed.
|
|
7
7
|
|
|
8
8
|
Parsing is defensive: a missing or malformed reply yields no verdicts rather
|
|
9
9
|
than raising, and unknown status values fall back to UNKNOWN.
|
|
@@ -75,7 +75,7 @@ def _render_capability(cap: Capability) -> str:
|
|
|
75
75
|
def _build_prompt(path: str, content: str, cap: Capability, context: str = "") -> str:
|
|
76
76
|
sub_names = ", ".join(cap.sub_capabilities) or "(none)"
|
|
77
77
|
context_block = (
|
|
78
|
-
f"Related code (call sites / usages elsewhere
|
|
78
|
+
f"Related code (call sites / usages elsewhere, for tracing where values come from, "
|
|
79
79
|
f"NOT under review):\n```\n{context}\n```\n\n"
|
|
80
80
|
if context
|
|
81
81
|
else ""
|
|
@@ -90,7 +90,7 @@ def _build_prompt(path: str, content: str, cap: Capability, context: str = "") -
|
|
|
90
90
|
"For input-driven issues (injection, path traversal, SSRF), mark VULNERABLE only when "
|
|
91
91
|
"untrusted/external input could plausibly reach the sink in the code shown. A constant, "
|
|
92
92
|
"a stored data field, a value from trusted config, or a path or argument the operator "
|
|
93
|
-
"supplies (e.g. a CLI argument) is not attacker-controlled
|
|
93
|
+
"supplies (e.g. a CLI argument) is not attacker-controlled; do not flag it.\n\n"
|
|
94
94
|
"Respond with a single JSON object exactly like:\n" + _JSON_SHAPE
|
|
95
95
|
)
|
|
96
96
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Static analysis for provenance (P1).
|
|
2
2
|
|
|
3
|
-
The code-graph / data-flow engine that gives the verifier provenance
|
|
3
|
+
The code-graph / data-flow engine that gives the verifier provenance: whether a
|
|
4
4
|
value reaching a sink is attacker-controlled, sanitized, or a trusted constant.
|
|
5
5
|
This is the real fix for the taint precision floor that single-file LLM review
|
|
6
6
|
cannot reach (see ROADMAP P1). Python / AST based to start.
|
|
@@ -8,7 +8,7 @@ and literals a value derives from.
|
|
|
8
8
|
The analysis is deliberately conservative and flow-insensitive: a name assigned
|
|
9
9
|
more than once contributes the union of all its right-hand sides, so a possible
|
|
10
10
|
source is never dropped (recall over precision). A value built only from literals
|
|
11
|
-
is reported as ``is_constant
|
|
11
|
+
is reported as ``is_constant``, the signal that distinguishes, for example,
|
|
12
12
|
SQL concatenated from constants (safe) from SQL concatenated from a parameter.
|
|
13
13
|
|
|
14
14
|
This module finds where a value comes from; it does not decide what is a source
|
|
@@ -43,7 +43,7 @@ class Origin:
|
|
|
43
43
|
|
|
44
44
|
@property
|
|
45
45
|
def is_constant(self) -> bool:
|
|
46
|
-
"""True when the value is built only from literals
|
|
46
|
+
"""True when the value is built only from literals: no param, call, attr,
|
|
47
47
|
free name, or unmodelled expression contributes."""
|
|
48
48
|
return not (self.params or self.calls or self.attrs or self.globals_ or self.unknown)
|
|
49
49
|
|
|
@@ -65,7 +65,7 @@ def parse_function(source: str, name: str) -> ast.FunctionDef | ast.AsyncFunctio
|
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
def find_calls(scope: ast.AST, callee: str) -> list[ast.Call]:
|
|
68
|
-
"""Every call within ``scope`` whose function is named ``callee
|
|
68
|
+
"""Every call within ``scope`` whose function is named ``callee``, matching a
|
|
69
69
|
bare name (``open``) or the final attribute (``execute`` in ``cur.execute``)."""
|
|
70
70
|
return [node for node in ast.walk(scope) if isinstance(node, ast.Call) and _call_name(node) == callee]
|
|
71
71
|
|
|
@@ -97,7 +97,7 @@ def trace_value(func: ast.FunctionDef | ast.AsyncFunctionDef, expr: ast.AST) ->
|
|
|
97
97
|
on_param=lambda n: Origin(params=frozenset({n})),
|
|
98
98
|
on_global=lambda n: Origin(globals_=frozenset({n})),
|
|
99
99
|
on_constant=lambda: _LITERAL,
|
|
100
|
-
on_cycle=Origin, # assignment cycle
|
|
100
|
+
on_cycle=Origin, # assignment cycle, no new information
|
|
101
101
|
)
|
|
102
102
|
|
|
103
103
|
|
|
@@ -182,7 +182,7 @@ def reduce_value(
|
|
|
182
182
|
if isinstance(expr, (ast.List, ast.Tuple, ast.Set)):
|
|
183
183
|
return combine([recurse(e) for e in expr.elts])
|
|
184
184
|
if isinstance(expr, ast.Name):
|
|
185
|
-
if expr.id in seen: # assignment cycle
|
|
185
|
+
if expr.id in seen: # assignment cycle, stop
|
|
186
186
|
return on_cycle()
|
|
187
187
|
if expr.id in params:
|
|
188
188
|
return on_param(expr.id)
|
|
@@ -6,7 +6,7 @@ sanitizer makes it SANITIZED (taint stops), a propagator carries taint through t
|
|
|
6
6
|
the result, and a trusted origin or a literal is clean.
|
|
7
7
|
|
|
8
8
|
The point is to let a later layer (P1-04) downgrade a taint finding only when the
|
|
9
|
-
value is *provably* not attacker-controlled
|
|
9
|
+
value is *provably* not attacker-controlled (``classification in SAFE``), so
|
|
10
10
|
recall is preserved: anything uncertain is UNKNOWN or PARAM, never quietly safe.
|
|
11
11
|
|
|
12
12
|
Two documented precision leans: a bare module-global name (e.g. ``STATIC_DIR``)
|
|
@@ -42,7 +42,7 @@ from codejury.resources import TAINT_FILE
|
|
|
42
42
|
|
|
43
43
|
class Taint(str, Enum):
|
|
44
44
|
EXTERNAL = "external" # derives from an attacker source, not sanitized
|
|
45
|
-
UNKNOWN = "unknown" # an unknown call / access
|
|
45
|
+
UNKNOWN = "unknown" # an unknown call / access; cannot prove either way
|
|
46
46
|
PARAM = "param" # depends on a parameter; resolve at the call site (cross-file)
|
|
47
47
|
SANITIZED = "sanitized" # had an external component, but a sanitizer neutralized it
|
|
48
48
|
TRUSTED = "trusted" # operator/config/global origin
|
|
@@ -131,14 +131,14 @@ def taint_of(
|
|
|
131
131
|
return Taint.TRUSTED
|
|
132
132
|
if _callee_in(node, vocab.propagators) or _callee_in(node, vocab.safe_sinks):
|
|
133
133
|
return _combine([recurse(a) for a in node.args])
|
|
134
|
-
return Taint.UNKNOWN # unknown call
|
|
134
|
+
return Taint.UNKNOWN # unknown call; a cross-file hop may resolve it later
|
|
135
135
|
if isinstance(node, (ast.Attribute, ast.Subscript)):
|
|
136
136
|
path = access_path(node)
|
|
137
137
|
if path and _access_in(path, vocab.sources):
|
|
138
138
|
return Taint.EXTERNAL
|
|
139
139
|
if path and _access_in(path, vocab.trusted):
|
|
140
140
|
return Taint.TRUSTED
|
|
141
|
-
if access_root(node) in params: # attribute of a parameter
|
|
141
|
+
if access_root(node) in params: # attribute of a parameter; resolve at the call site
|
|
142
142
|
return on_param(access_root(node))
|
|
143
143
|
return Taint.UNKNOWN # unknown object attribute (e.g. self.x): not provably safe
|
|
144
144
|
return Taint.UNKNOWN
|
|
@@ -150,7 +150,7 @@ def taint_of(
|
|
|
150
150
|
combine=_combine,
|
|
151
151
|
leaf=leaf,
|
|
152
152
|
on_param=on_param,
|
|
153
|
-
on_global=lambda n: Taint.TRUSTED, # free module global / builtin
|
|
153
|
+
on_global=lambda n: Taint.TRUSTED, # free module global / builtin; conventionally a constant
|
|
154
154
|
on_constant=lambda: Taint.CONSTANT,
|
|
155
155
|
on_cycle=lambda: Taint.CONSTANT, # assignment cycle: no new information
|
|
156
156
|
)
|
|
@@ -261,7 +261,7 @@ def worst_sink_taint(content: str, files: dict[str, str], vocab: TaintVocab) ->
|
|
|
261
261
|
A "potential sink" is any call that is not a safe sink, sanitizer, or
|
|
262
262
|
propagator (those are not where injection happens). Each such call's argument
|
|
263
263
|
taint is classified with the cross-file resolver. A safe sink that consumes
|
|
264
|
-
tainted data (e.g. ``json.loads(request.data)``) contributes SANITIZED
|
|
264
|
+
tainted data (e.g. ``json.loads(request.data)``) contributes SANITIZED: the
|
|
265
265
|
data was handled safely. The worst contribution is returned.
|
|
266
266
|
|
|
267
267
|
``Taint.UNKNOWN`` when no inspectable sink is found (the artifact may still be
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Assembly
|
|
1
|
+
"""Assembly: build an orchestration from a strategy name and run it over a source.
|
|
2
2
|
|
|
3
3
|
Shared by the CLI and the task layer so the "which agents + which orchestrator"
|
|
4
4
|
mapping and the per-artifact run loop live in one place.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
"""Diff baseline
|
|
1
|
+
"""Diff baseline: report only findings new since a stored baseline report.
|
|
2
2
|
|
|
3
|
-
The keystone for PR-time noise control: run against a saved baseline report
|
|
4
|
-
target branch's findings
|
|
3
|
+
The keystone for PR-time noise control: run against a saved baseline report, the
|
|
4
|
+
target branch's findings, and keep only the problem observations whose
|
|
5
5
|
fingerprint is absent from the baseline, so a review shows what this change
|
|
6
|
-
introduced
|
|
6
|
+
introduced rather than the codebase's pre-existing findings. Paired with --fail-on,
|
|
7
7
|
CI then gates on new issues only.
|
|
8
8
|
|
|
9
|
-
The fingerprint is line-number-tolerant
|
|
9
|
+
The fingerprint is line-number-tolerant because lines shift between versions: it keys
|
|
10
10
|
on the capability, the kind/severity/status, the matched patterns, and the
|
|
11
|
-
normalized evidence snippet
|
|
12
|
-
|
|
13
|
-
NOT_PRESENT verdicts and concessions are always kept.
|
|
11
|
+
normalized evidence snippet, never the line number. Only problem observations,
|
|
12
|
+
namely Findings and VULNERABLE/PARTIAL Verdicts, are compared and dropped; SECURE
|
|
13
|
+
and NOT_PRESENT verdicts and concessions are always kept.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
@@ -52,7 +52,7 @@ def filter_new(results: Results, baseline: Results) -> tuple[Results, int]:
|
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
def _evidence_sig(o: Observation) -> str:
|
|
55
|
-
# fingerprints only the first evidence snippet
|
|
55
|
+
# fingerprints only the first evidence snippet, enough to distinguish findings
|
|
56
56
|
# in practice; two findings differing only in a later snippet would collide.
|
|
57
57
|
evidence = getattr(o, "evidence", [])
|
|
58
58
|
return " ".join(evidence[0].code.split()) if evidence and evidence[0].code else ""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
id: business_logic
|
|
2
2
|
name: Business Logic
|
|
3
3
|
asvs_chapter: V11
|
|
4
|
-
description: Correctness of stateful workflows against abuse
|
|
4
|
+
description: Correctness of stateful workflows against abuse, covering ordering, races, replay, and value validation.
|
|
5
5
|
|
|
6
6
|
sub_capabilities:
|
|
7
7
|
state_and_sequence:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
id: dependency_config
|
|
2
2
|
name: Dependencies and Configuration
|
|
3
3
|
asvs_chapter: V14
|
|
4
|
-
description: Software supply chain and deployment configuration
|
|
4
|
+
description: Software supply chain and deployment configuration, covering known-vulnerable components and unsafe defaults.
|
|
5
5
|
|
|
6
6
|
sub_capabilities:
|
|
7
7
|
dependencies:
|
|
@@ -50,7 +50,7 @@ sub_capabilities:
|
|
|
50
50
|
correct_patterns:
|
|
51
51
|
- id: TLS-OK-1
|
|
52
52
|
description: >-
|
|
53
|
-
Leave TLS certificate verification at its secure default
|
|
53
|
+
Leave TLS certificate verification at its secure default: verify omitted or
|
|
54
54
|
verify=True, the default SSL context, hostname checking on
|
|
55
55
|
signals: ["verify=True", "create_default_context", "requests.get(", "requests.post("]
|
|
56
56
|
why_ok: >-
|
|
@@ -63,7 +63,7 @@ sub_capabilities:
|
|
|
63
63
|
cwe: CWE-295
|
|
64
64
|
severity: HIGH
|
|
65
65
|
description: >-
|
|
66
|
-
Disable TLS certificate or hostname verification
|
|
66
|
+
Disable TLS certificate or hostname verification: verify=False, CERT_NONE,
|
|
67
67
|
check_hostname=False, or an unverified SSL context
|
|
68
68
|
signals: ["verify=False", "CERT_NONE", "check_hostname = False", "_create_unverified_context"]
|
|
69
69
|
why_bad: An unverified TLS connection is open to a man-in-the-middle despite https://
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# OWASP LLM Top 10 (2025)
|
|
1
|
+
# OWASP LLM Top 10 (2025), LLM06: Excessive Agency.
|
|
2
2
|
id: excessive_agency
|
|
3
3
|
name: Excessive Agency
|
|
4
4
|
asvs_chapter: "" # OWASP LLM06, not an ASVS chapter
|
|
5
5
|
description: >-
|
|
6
6
|
An LLM-driven agent acts on the world from model output. The risk is too much
|
|
7
7
|
autonomy or privilege: a completion (which an attacker can steer via prompt
|
|
8
|
-
injection) triggers a high-impact or irreversible action
|
|
9
|
-
send, run
|
|
8
|
+
injection) triggers a high-impact or irreversible action, such as delete, transfer,
|
|
9
|
+
send, or run, with no allowlist of permitted tools, no human confirmation, and
|
|
10
10
|
no least-privilege scoping. The fix is to gate actions: allowlist low-impact
|
|
11
11
|
tools, require human approval for high-impact ones, and scope each tool's
|
|
12
12
|
authority narrowly. This is about the action and its authority, not about
|
|
@@ -54,7 +54,7 @@ sub_capabilities:
|
|
|
54
54
|
cwe: CWE-78
|
|
55
55
|
severity: CRITICAL
|
|
56
56
|
description: >-
|
|
57
|
-
Pass interpolated input to an OS shell or subprocess
|
|
57
|
+
Pass interpolated input to an OS shell or subprocess: os.system, os.popen,
|
|
58
58
|
subprocess(..., shell=True), or eval/exec. A normal function, method, or
|
|
59
59
|
library/API call is NOT this; flag only an actual shell or process invocation.
|
|
60
60
|
signals: ["os.system(", "shell=True", "os.popen("]
|
|
@@ -80,7 +80,7 @@ sub_capabilities:
|
|
|
80
80
|
|
|
81
81
|
- id: PATH-OK-2
|
|
82
82
|
description: >-
|
|
83
|
-
Use a path that is not attacker-controlled
|
|
83
|
+
Use a path that is not attacker-controlled: a data field, a directory read from
|
|
84
84
|
trusted config, or a path the operator passes on the command line
|
|
85
85
|
why_ok: >-
|
|
86
86
|
Traversal needs an external attacker to control the path. A path stored as a
|
|
@@ -114,7 +114,7 @@ sub_capabilities:
|
|
|
114
114
|
|
|
115
115
|
- id: SSRF-OK-2
|
|
116
116
|
description: >-
|
|
117
|
-
Fetch a URL that is not attacker-controlled
|
|
117
|
+
Fetch a URL that is not attacker-controlled: a constant, a value from trusted
|
|
118
118
|
config, or an operator-supplied argument
|
|
119
119
|
why_ok: >-
|
|
120
120
|
SSRF needs an external attacker to control the destination. A constant URL or one
|
|
@@ -130,7 +130,7 @@ sub_capabilities:
|
|
|
130
130
|
constant URL, one from trusted config, or an operator-supplied argument.
|
|
131
131
|
signals: ["requests.get(", "urllib.request.urlopen(", "httpx.", "request.args", "request.json"]
|
|
132
132
|
why_bad: >-
|
|
133
|
-
The server makes the request, so attacker input reaches internal-only targets
|
|
133
|
+
The server makes the request, so attacker input reaches internal-only targets:
|
|
134
134
|
cloud metadata, localhost admin ports, internal APIs behind the firewall.
|
|
135
135
|
example_bad: |
|
|
136
136
|
requests.get(request.args["url"]).text
|
|
@@ -143,7 +143,7 @@ sub_capabilities:
|
|
|
143
143
|
correct_patterns:
|
|
144
144
|
- id: DESER-OK-1
|
|
145
145
|
description: >-
|
|
146
|
-
Parse untrusted input with a data-only parser
|
|
146
|
+
Parse untrusted input with a data-only parser, json.loads or yaml.safe_load,
|
|
147
147
|
that cannot instantiate arbitrary objects
|
|
148
148
|
signals: ["json.loads", "yaml.safe_load"]
|
|
149
149
|
why_ok: A data-only parser builds plain structures and has no code-execution path
|
|
@@ -153,7 +153,7 @@ sub_capabilities:
|
|
|
153
153
|
cwe: CWE-502
|
|
154
154
|
severity: CRITICAL
|
|
155
155
|
description: >-
|
|
156
|
-
Deserialize externally controlled bytes with an object-constructing deserializer
|
|
156
|
+
Deserialize externally controlled bytes with an object-constructing deserializer:
|
|
157
157
|
pickle, marshal, yaml.load (unsafe Loader), or jsonpickle. NOT this: a data-only
|
|
158
158
|
parser like json.loads or yaml.safe_load.
|
|
159
159
|
signals: ["pickle.loads", "pickle.load(", "yaml.load(", "marshal.loads", "jsonpickle.decode"]
|
{codejury-0.10.1 → codejury-0.10.2}/codejury/data/capabilities/insecure_output_handling.yaml
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# OWASP LLM Top 10 (2025)
|
|
1
|
+
# OWASP LLM Top 10 (2025), LLM05: Improper Output Handling.
|
|
2
2
|
id: insecure_output_handling
|
|
3
3
|
name: Insecure Output Handling
|
|
4
4
|
asvs_chapter: "" # OWASP LLM05, not an ASVS chapter
|
|
5
5
|
description: >-
|
|
6
6
|
Model output is untrusted. When a completion is passed to a downstream
|
|
7
7
|
interpreter (eval/exec, a shell, SQL) or rendered as markup without validation
|
|
8
|
-
or encoding, the model
|
|
8
|
+
or encoding, the model, or an attacker who steered it via prompt injection,
|
|
9
9
|
can reach code execution or XSS. Constrain and validate model output before use;
|
|
10
10
|
encode it before rendering.
|
|
11
11
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# OWASP LLM Top 10 (2025)
|
|
1
|
+
# OWASP LLM Top 10 (2025), LLM01: Prompt Injection.
|
|
2
2
|
id: prompt_injection
|
|
3
3
|
name: Prompt Injection
|
|
4
4
|
asvs_chapter: "" # OWASP LLM01, not an ASVS chapter
|
|
5
5
|
description: >-
|
|
6
|
-
Untrusted text (the end user's input, or content the app retrieves
|
|
6
|
+
Untrusted text (the end user's input, or content the app retrieves: web pages,
|
|
7
7
|
documents, tool/function results, RAG chunks) reaches the model in a position
|
|
8
8
|
where the model can treat it as instructions. The fix is separation and least
|
|
9
9
|
authority: keep untrusted content as data, never concatenate it into the
|
|
@@ -11,13 +11,13 @@ sub_capabilities:
|
|
|
11
11
|
signals: ["os.environ[", "os.getenv(", "secretsmanager", "vault"]
|
|
12
12
|
why_ok: >-
|
|
13
13
|
A variable that reads its value from the environment or a secret manager is the
|
|
14
|
-
correct pattern, not a violation
|
|
14
|
+
correct pattern, not a violation; nothing secret is written in the source.
|
|
15
15
|
|
|
16
16
|
- id: SEC-OK-2
|
|
17
17
|
description: Receive a secret as a function or constructor parameter (dependency injection)
|
|
18
18
|
signals: ["def __init__(self, *, api_key", "api_key: str | None = None", "api_key=api_key"]
|
|
19
19
|
why_ok: >-
|
|
20
|
-
Accepting or forwarding a key through a parameter or variable is correct
|
|
20
|
+
Accepting or forwarding a key through a parameter or variable is correct; the value
|
|
21
21
|
comes from the caller or the environment, not a literal in the source. Only an actual
|
|
22
22
|
key string written in the code is a finding.
|
|
23
23
|
|
|
@@ -26,7 +26,7 @@ sub_capabilities:
|
|
|
26
26
|
cwe: CWE-798
|
|
27
27
|
severity: HIGH
|
|
28
28
|
description: >-
|
|
29
|
-
Assign a literal credential string
|
|
29
|
+
Assign a literal credential string: an actual key/token value written in the source.
|
|
30
30
|
A variable, parameter, env lookup, or a non-credential string (e.g. a model name or
|
|
31
31
|
URL) that merely holds or forwards a value is NOT this.
|
|
32
32
|
signals: ['api_key = "sk', 'token = "ghp_', 'aws_secret_access_key = "']
|
|
@@ -34,8 +34,8 @@ sub_capabilities:
|
|
|
34
34
|
example_bad: |
|
|
35
35
|
API_KEY = "sk_live_51HxQ....actual-secret-value" # literal secret in source
|
|
36
36
|
example_good: |
|
|
37
|
-
api_key = os.environ["API_KEY"] # read from env
|
|
38
|
-
client = Client(api_key=api_key) # passed as a parameter
|
|
37
|
+
api_key = os.environ["API_KEY"] # read from env, fine
|
|
38
|
+
client = Client(api_key=api_key) # passed as a parameter, fine
|
|
39
39
|
|
|
40
40
|
- id: SEC-BAD-2
|
|
41
41
|
cwe: CWE-259
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Adversarial negative: the model chooses an action, but only among a fixed set
|
|
2
|
-
# of low-impact, read-only actions
|
|
2
|
+
# of low-impact, read-only actions: no arbitrary dispatch, no high-impact op.
|
|
3
3
|
capability: excessive_agency
|
|
4
4
|
vulnerable: false
|
|
5
5
|
expected_verdict: SECURE
|