codejury 0.10.3__tar.gz → 0.10.5__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.3 → codejury-0.10.5}/PKG-INFO +2 -2
- {codejury-0.10.3 → codejury-0.10.5}/README.md +1 -1
- codejury-0.10.5/codejury/analysis/__init__.py +8 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/analysis/taint.py +27 -10
- {codejury-0.10.3 → codejury-0.10.5}/codejury/cli.py +5 -1
- codejury-0.10.5/codejury/data/capabilities/model_supply_chain.yaml +59 -0
- codejury-0.10.5/codejury/data/golden/sc_pinned_safe.yaml +11 -0
- codejury-0.10.5/codejury/data/golden/sc_safetensors_safe.yaml +12 -0
- codejury-0.10.5/codejury/data/golden/sc_torch_load_vuln.yaml +11 -0
- codejury-0.10.5/codejury/data/golden/sc_trust_remote_code_vuln.yaml +11 -0
- codejury-0.10.5/codejury/infrastructure/__init__.py +4 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/infrastructure/cache.py +17 -3
- codejury-0.10.5/codejury/orchestrators/__init__.py +6 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/orchestrators/challenge.py +9 -2
- {codejury-0.10.3 → codejury-0.10.5}/codejury/reporting.py +1 -2
- {codejury-0.10.3 → codejury-0.10.5}/codejury/tasks/base.py +11 -2
- {codejury-0.10.3 → codejury-0.10.5}/codejury.egg-info/PKG-INFO +2 -2
- {codejury-0.10.3 → codejury-0.10.5}/codejury.egg-info/SOURCES.txt +6 -0
- {codejury-0.10.3 → codejury-0.10.5}/pyproject.toml +1 -1
- codejury-0.10.5/tests/test_openai_format.py +30 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_review_fixes.py +26 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_taint.py +12 -0
- codejury-0.10.3/codejury/analysis/__init__.py +0 -7
- codejury-0.10.3/codejury/infrastructure/__init__.py +0 -4
- codejury-0.10.3/codejury/orchestrators/__init__.py +0 -6
- {codejury-0.10.3 → codejury-0.10.5}/LICENSE +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/__init__.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/agents/__init__.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/agents/base.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/agents/debate.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/agents/mock.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/agents/parsing.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/agents/refuter.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/agents/verifier.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/analysis/provenance.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/assembly.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/baseline.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/authentication.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/authorization.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/business_logic.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/crypto.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/data_protection.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/dependency_config.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/error_logging.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/excessive_agency.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/input_validation.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/insecure_output_handling.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/output_encoding.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/prompt_injection.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/secrets.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/session.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ag_allowlist_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ag_arbitrary_tool_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ag_destructive_no_confirm_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ag_fixed_enum_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ag_human_approval_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ag_model_confirmed_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/authn_bcrypt_password.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/authn_jwt_noverify_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/authn_jwt_verified_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/authn_sha256_checksum_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/authn_sha256_password.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/authn_weak_hash_indirect_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/authz_idor_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/authz_owner_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/business_logic_price_tamper_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/business_logic_server_checked_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/cmdi_fixed_argv_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/cmdi_ossystem_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/cmdi_subprocess_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/crypto_aesgcm_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/crypto_ecb_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/data_protection_plaintext_pii_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/data_protection_tokenized_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/dependency_config_tls_verify_off_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/dependency_config_tls_verify_on_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/deserialize_json_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/deserialize_pickle_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/error_logging_redacted_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/error_logging_secret_leak_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ioh_escaped_output_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ioh_exec_output_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ioh_innerhtml_output_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ioh_json_response_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ioh_output_to_sql_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ioh_schema_validated_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/literal_eval_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/path_basename_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/path_contained_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/path_traversal_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/pi_delimited_data_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/pi_format_role_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/pi_indirect_rag_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/pi_system_concat_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/pi_user_content_concat_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/pi_user_role_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/secrets_env_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/secrets_hardcoded_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/session_fixation_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/session_secure_cookie_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/sql_constant_concat_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/sqli_format_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/sqli_fstring_query.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/sqli_indirect_var_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/sqli_parameterized_query.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ssrf_allowlist_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ssrf_constant_url_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ssrf_substring_allowlist_bypass_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ssrf_user_url_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/xfile_idor_no_check_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/xfile_idor_owner_checked_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/xfile_path_sanitized_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/xfile_path_tainted_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/xss_innerhtml_constant_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/xss_innerhtml_vuln.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/xss_textcontent_safe.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/suppressions.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/taint.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/tasks/audit_diff_debate.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/data/tasks/quick_scan_single.yaml +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/domain/__init__.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/domain/artifact.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/domain/capability.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/domain/context.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/domain/observation.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/domain/result.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/evaluation.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/infrastructure/json_parse.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/integrations/__init__.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/integrations/github.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/orchestrators/adaptive.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/orchestrators/base.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/orchestrators/debate.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/orchestrators/pipeline.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/orchestrators/reflexion.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/orchestrators/single.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/orchestrators/taint_gate.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/providers/__init__.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/providers/anthropic.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/providers/base.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/providers/litellm.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/providers/mock.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/providers/openai.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/providers/openai_format.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/providers/retry.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/resources.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/sources/__init__.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/sources/base.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/sources/callers.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/sources/chunker.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/sources/diff.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/sources/function.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/sources/mock.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/sources/repo.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/suppression.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/tasks/__init__.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury/tasks/registry.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury.egg-info/dependency_links.txt +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury.egg-info/entry_points.txt +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury.egg-info/requires.txt +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/codejury.egg-info/top_level.txt +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/setup.cfg +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_adaptive.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_anthropic_provider.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_assembly.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_audit_pipeline.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_baseline.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_cache.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_callers.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_capability.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_challenge.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_cli_audit.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_context.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_debate_agents.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_debate_orchestrator.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_diff_source.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_evaluation.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_function_source.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_integrations.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_json_parse.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_litellm_provider.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_openai_provider.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_orchestrator.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_parsing.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_pipeline_orchestrator.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_provenance.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_reflexion_orchestrator.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_repo_source.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_reporting.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_retry_provider.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_sarif.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_suppression.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_taint_crossfile.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_taint_gate.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/tests/test_tasks.py +0 -0
- {codejury-0.10.3 → codejury-0.10.5}/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.5
|
|
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
|
|
@@ -186,7 +186,7 @@ what `--only` and a task's `capabilities:` accept:
|
|
|
186
186
|
`authn`, `authz`, `session`, `input_validation`, `output_encoding`, `crypto`,
|
|
187
187
|
`secrets`, `data_protection`, `error_logging`, `business_logic`,
|
|
188
188
|
`dependency_config`, `prompt_injection`, `insecure_output_handling`,
|
|
189
|
-
`excessive_agency`.
|
|
189
|
+
`excessive_agency`, `model_supply_chain`.
|
|
190
190
|
|
|
191
191
|
To tune for your codebase, edit these files, adding patterns or sharpening
|
|
192
192
|
wording. No code change is needed.
|
|
@@ -156,7 +156,7 @@ what `--only` and a task's `capabilities:` accept:
|
|
|
156
156
|
`authn`, `authz`, `session`, `input_validation`, `output_encoding`, `crypto`,
|
|
157
157
|
`secrets`, `data_protection`, `error_logging`, `business_logic`,
|
|
158
158
|
`dependency_config`, `prompt_injection`, `insecure_output_handling`,
|
|
159
|
-
`excessive_agency`.
|
|
159
|
+
`excessive_agency`, `model_supply_chain`.
|
|
160
160
|
|
|
161
161
|
To tune for your codebase, edit these files, adding patterns or sharpening
|
|
162
162
|
wording. No code change is needed.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Static analysis for provenance (P1).
|
|
2
|
+
|
|
3
|
+
The code-graph / data-flow engine that gives the verifier provenance: whether a
|
|
4
|
+
value reaching a sink is attacker-controlled, sanitized, or a trusted constant.
|
|
5
|
+
It targets the taint precision floor that single-file LLM review cannot reach,
|
|
6
|
+
see ROADMAP P1. Python AST based, currently intra-procedural plus a one-hop
|
|
7
|
+
cross-file caller resolution, not yet a full code graph.
|
|
8
|
+
"""
|
|
@@ -130,7 +130,15 @@ def taint_of(
|
|
|
130
130
|
if func_path and _access_in(func_path, vocab.trusted):
|
|
131
131
|
return Taint.TRUSTED
|
|
132
132
|
if _callee_in(node, vocab.propagators) or _callee_in(node, vocab.safe_sinks):
|
|
133
|
-
|
|
133
|
+
parts = [recurse(a) for a in node.args]
|
|
134
|
+
# a method-form propagator carries the receiver's taint too, e.g.
|
|
135
|
+
# request.args.get("id").strip(). Recurse the receiver when it is a
|
|
136
|
+
# value (Name/Call/Subscript), not a module path like os.path.join.
|
|
137
|
+
if isinstance(node.func, ast.Attribute) and isinstance(
|
|
138
|
+
node.func.value, (ast.Name, ast.Call, ast.Subscript)
|
|
139
|
+
):
|
|
140
|
+
parts.append(recurse(node.func.value))
|
|
141
|
+
return _combine(parts)
|
|
134
142
|
return Taint.UNKNOWN # unknown call; a cross-file hop may resolve it later
|
|
135
143
|
if isinstance(node, (ast.Attribute, ast.Subscript)):
|
|
136
144
|
path = access_path(node)
|
|
@@ -242,17 +250,26 @@ def _call_sites(name: str, files: dict[str, str]) -> list[tuple[ast.AST, ast.Cal
|
|
|
242
250
|
tree = _parse(source)
|
|
243
251
|
if tree is None:
|
|
244
252
|
continue
|
|
245
|
-
|
|
253
|
+
scopes = _scope_index(tree)
|
|
246
254
|
for call in find_calls(tree, name):
|
|
247
|
-
sites.append((
|
|
255
|
+
sites.append((scopes.get(id(call), tree), call))
|
|
248
256
|
return sites
|
|
249
257
|
|
|
250
258
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
259
|
+
@functools.lru_cache(maxsize=256)
|
|
260
|
+
def _scope_index(tree: ast.Module) -> dict[int, ast.AST]:
|
|
261
|
+
"""Map id(node) to its innermost enclosing function (or the module tree) in one
|
|
262
|
+
pass, so a call's scope is an O(1) lookup instead of an O(functions) re-walk."""
|
|
263
|
+
mapping: dict[int, ast.AST] = {}
|
|
264
|
+
|
|
265
|
+
def walk(node: ast.AST, scope: ast.AST) -> None:
|
|
266
|
+
inner = node if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) else scope
|
|
267
|
+
for child in ast.iter_child_nodes(node):
|
|
268
|
+
mapping[id(child)] = inner
|
|
269
|
+
walk(child, inner)
|
|
270
|
+
|
|
271
|
+
walk(tree, tree)
|
|
272
|
+
return mapping
|
|
256
273
|
|
|
257
274
|
|
|
258
275
|
def worst_sink_taint(content: str, files: dict[str, str], vocab: TaintVocab) -> Taint | None:
|
|
@@ -272,7 +289,7 @@ def worst_sink_taint(content: str, files: dict[str, str], vocab: TaintVocab) ->
|
|
|
272
289
|
tree = _parse(content)
|
|
273
290
|
if tree is None:
|
|
274
291
|
return None
|
|
275
|
-
|
|
292
|
+
scopes = _scope_index(tree)
|
|
276
293
|
taints: list[Taint] = []
|
|
277
294
|
for call in [n for n in ast.walk(tree) if isinstance(n, ast.Call)]:
|
|
278
295
|
if is_safe_sink(call, vocab):
|
|
@@ -280,7 +297,7 @@ def worst_sink_taint(content: str, files: dict[str, str], vocab: TaintVocab) ->
|
|
|
280
297
|
continue
|
|
281
298
|
if _callee_in(call, vocab.sanitizers) or _callee_in(call, vocab.propagators):
|
|
282
299
|
continue # not a sink itself; counted via the enclosing sink's argument
|
|
283
|
-
scope =
|
|
300
|
+
scope = scopes.get(id(call), tree)
|
|
284
301
|
for arg in (*call.args, *(kw.value for kw in call.keywords)):
|
|
285
302
|
taints.append(taint_in_repo(scope, arg, vocab, files))
|
|
286
303
|
return _combine(taints) if taints else Taint.UNKNOWN
|
|
@@ -290,6 +290,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
290
290
|
run_p.add_argument("--capabilities", default=CAPABILITIES_DIR, help="capability YAML directory")
|
|
291
291
|
run_p.add_argument("--format", choices=_FORMATS, default="text", dest="fmt")
|
|
292
292
|
run_p.add_argument("--no-suppress", action="store_true", help="disable the known-noise suppression filter")
|
|
293
|
+
run_p.add_argument("--no-cache", action="store_true", help="bypass the verdict cache (always re-query the model)")
|
|
293
294
|
run_p.add_argument("--fail-on", choices=_FAIL_ON, default=None, dest="fail_on", help="exit 1 if a finding at/above this severity is found")
|
|
294
295
|
|
|
295
296
|
eval_p = sub.add_parser("eval", help="score golden cases and report precision/recall")
|
|
@@ -366,7 +367,10 @@ def _dispatch(args, parser) -> int:
|
|
|
366
367
|
print(f"unknown task {args.task!r}; available: {', '.join(sorted(tasks)) or '(none)'}")
|
|
367
368
|
return 1
|
|
368
369
|
results = run_task(
|
|
369
|
-
tasks[args.task],
|
|
370
|
+
tasks[args.task],
|
|
371
|
+
DiffSource(_read_diff(args.diff)),
|
|
372
|
+
load_capabilities(args.capabilities),
|
|
373
|
+
cache=None if args.no_cache else VerdictCache(),
|
|
370
374
|
)
|
|
371
375
|
results = _maybe_suppress(results, not args.no_suppress)
|
|
372
376
|
print(_render_results(args.fmt, results))
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# OWASP LLM Top 10 (2025), LLM03: Supply Chain.
|
|
2
|
+
id: model_supply_chain
|
|
3
|
+
name: Model Supply Chain
|
|
4
|
+
asvs_chapter: "" # OWASP LLM03, not an ASVS chapter
|
|
5
|
+
description: >-
|
|
6
|
+
Loading a model, its code, or its weights from an untrusted or unverified
|
|
7
|
+
source. The high-impact cases run code at load time: executing code shipped
|
|
8
|
+
with a third-party model, or deserializing weights through pickle. Pin and
|
|
9
|
+
verify artifacts, and do not enable remote code execution for a model you do
|
|
10
|
+
not control.
|
|
11
|
+
|
|
12
|
+
sub_capabilities:
|
|
13
|
+
remote_code:
|
|
14
|
+
correct_patterns:
|
|
15
|
+
- id: SC-OK-1
|
|
16
|
+
description: Load a model with remote code execution off and a pinned revision
|
|
17
|
+
signals: ["trust_remote_code=False", "revision="]
|
|
18
|
+
why_ok: The third-party repo cannot run code at load time and the artifact is pinned
|
|
19
|
+
|
|
20
|
+
anti_patterns:
|
|
21
|
+
- id: SC-BAD-1
|
|
22
|
+
cwe: CWE-494
|
|
23
|
+
severity: CRITICAL
|
|
24
|
+
description: >-
|
|
25
|
+
Load a model with trust_remote_code=True, which executes code shipped in
|
|
26
|
+
the model repository at load time
|
|
27
|
+
signals: ["trust_remote_code=True", "from_pretrained"]
|
|
28
|
+
why_bad: A malicious or compromised model repo runs arbitrary code in your process
|
|
29
|
+
example_bad: |
|
|
30
|
+
model = AutoModel.from_pretrained("vendor/model", trust_remote_code=True)
|
|
31
|
+
example_good: |
|
|
32
|
+
model = AutoModel.from_pretrained("vendor/model", revision="a1b2c3d")
|
|
33
|
+
|
|
34
|
+
artifact_integrity:
|
|
35
|
+
correct_patterns:
|
|
36
|
+
- id: SC-OK-2
|
|
37
|
+
description: Load weights from a data-only format such as safetensors
|
|
38
|
+
signals: ["safetensors", "load_file"]
|
|
39
|
+
why_ok: A data-only weights format cannot execute code on load
|
|
40
|
+
|
|
41
|
+
anti_patterns:
|
|
42
|
+
- id: SC-BAD-2
|
|
43
|
+
cwe: CWE-502
|
|
44
|
+
severity: HIGH
|
|
45
|
+
description: >-
|
|
46
|
+
Deserialize model weights or a checkpoint through pickle, for example
|
|
47
|
+
torch.load or joblib.load on a downloaded file, which can run code on load
|
|
48
|
+
signals: ["torch.load", "pickle.load", "joblib.load"]
|
|
49
|
+
why_bad: Pickle-based loaders execute code embedded in the file during deserialization
|
|
50
|
+
example_bad: |
|
|
51
|
+
state = torch.load(downloaded_checkpoint)
|
|
52
|
+
example_good: |
|
|
53
|
+
from safetensors.torch import load_file
|
|
54
|
+
state = load_file("model.safetensors")
|
|
55
|
+
|
|
56
|
+
trigger_signals:
|
|
57
|
+
- from_pretrained or a model or dataset download call
|
|
58
|
+
- trust_remote_code set on a model load
|
|
59
|
+
- torch.load, pickle.load, or joblib.load of model weights
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
capability: model_supply_chain
|
|
2
|
+
vulnerable: false
|
|
3
|
+
expected_verdict: SECURE
|
|
4
|
+
cwe: ""
|
|
5
|
+
source: synthetic
|
|
6
|
+
notes: >
|
|
7
|
+
An ordinary model load: remote code execution is off by default and the
|
|
8
|
+
revision is pinned, so a plain from_pretrained must not be flagged.
|
|
9
|
+
code: |
|
|
10
|
+
def load():
|
|
11
|
+
return AutoModel.from_pretrained("bert-base-uncased", revision="a1b2c3d")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
capability: model_supply_chain
|
|
2
|
+
vulnerable: false
|
|
3
|
+
expected_verdict: SECURE
|
|
4
|
+
cwe: ""
|
|
5
|
+
source: synthetic
|
|
6
|
+
notes: >
|
|
7
|
+
safetensors is a data-only weights format that cannot execute code on load.
|
|
8
|
+
code: |
|
|
9
|
+
from safetensors.torch import load_file
|
|
10
|
+
|
|
11
|
+
def load_checkpoint(path):
|
|
12
|
+
return load_file(path)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
capability: model_supply_chain
|
|
2
|
+
vulnerable: true
|
|
3
|
+
expected_verdict: VULNERABLE
|
|
4
|
+
cwe: CWE-502
|
|
5
|
+
source: synthetic
|
|
6
|
+
notes: >
|
|
7
|
+
torch.load deserializes through pickle, so loading a downloaded checkpoint runs
|
|
8
|
+
any code embedded in the file. Use a data-only format such as safetensors.
|
|
9
|
+
code: |
|
|
10
|
+
def load_checkpoint(path):
|
|
11
|
+
return torch.load(path)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
capability: model_supply_chain
|
|
2
|
+
vulnerable: true
|
|
3
|
+
expected_verdict: VULNERABLE
|
|
4
|
+
cwe: CWE-494
|
|
5
|
+
source: synthetic
|
|
6
|
+
notes: >
|
|
7
|
+
trust_remote_code=True runs code shipped in the third-party model repo at load
|
|
8
|
+
time, so a malicious or compromised repo executes arbitrary code in the process.
|
|
9
|
+
code: |
|
|
10
|
+
def load(name):
|
|
11
|
+
return AutoModel.from_pretrained(name, trust_remote_code=True)
|
|
@@ -22,6 +22,7 @@ from __future__ import annotations
|
|
|
22
22
|
|
|
23
23
|
import hashlib
|
|
24
24
|
import json
|
|
25
|
+
import os
|
|
25
26
|
from pathlib import Path
|
|
26
27
|
|
|
27
28
|
from codejury.domain.artifact import CodeArtifact
|
|
@@ -30,6 +31,10 @@ from codejury.domain.result import AnalysisResult
|
|
|
30
31
|
|
|
31
32
|
DEFAULT_CACHE_DIR = Path.home() / ".cache" / "codejury" / "verdicts"
|
|
32
33
|
|
|
34
|
+
# Bump when the cached payload shape changes, so entries written by an older
|
|
35
|
+
# codejury are not read back under a changed schema. It is folded into the key.
|
|
36
|
+
_SCHEMA = "1"
|
|
37
|
+
|
|
33
38
|
|
|
34
39
|
def _normalize(code: str) -> str:
|
|
35
40
|
lines = code.replace("\r\n", "\n").replace("\r", "\n").split("\n")
|
|
@@ -40,6 +45,7 @@ def verdict_key(
|
|
|
40
45
|
artifact: CodeArtifact, capabilities: list[Capability], *, orchestration: str
|
|
41
46
|
) -> str:
|
|
42
47
|
payload = {
|
|
48
|
+
"schema": _SCHEMA,
|
|
43
49
|
"kind": artifact.kind,
|
|
44
50
|
"path": artifact.path,
|
|
45
51
|
"code": _normalize(artifact.content),
|
|
@@ -65,12 +71,20 @@ class VerdictCache:
|
|
|
65
71
|
path = self._dir / f"{key}.json"
|
|
66
72
|
if not path.exists():
|
|
67
73
|
return None
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
try:
|
|
75
|
+
with open(path, encoding="utf-8") as f:
|
|
76
|
+
return AnalysisResult.from_dict(json.load(f))
|
|
77
|
+
except (ValueError, OSError): # corrupt or half-written entry: treat as a miss
|
|
78
|
+
return None
|
|
70
79
|
|
|
71
80
|
def put(self, key: str, result: AnalysisResult) -> None:
|
|
72
81
|
if result.error: # never cache a partial/failed run
|
|
73
82
|
return
|
|
74
83
|
self._dir.mkdir(parents=True, exist_ok=True)
|
|
75
|
-
|
|
84
|
+
path = self._dir / f"{key}.json"
|
|
85
|
+
# write to a temp file then atomically rename, so a concurrent reader never
|
|
86
|
+
# sees a half-written entry
|
|
87
|
+
tmp = path.with_suffix(f".{os.getpid()}.tmp")
|
|
88
|
+
with open(tmp, "w", encoding="utf-8") as f:
|
|
76
89
|
json.dump(result.to_dict(), f, ensure_ascii=False)
|
|
90
|
+
os.replace(tmp, path)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""codejury.orchestrators: strategies for running agents over a context.
|
|
2
|
+
|
|
3
|
+
single, pipeline, debate, reflexion, challenge, taint, and adaptive. The strategy
|
|
4
|
+
is the orchestration axis; a task picks one. Each takes the same agents and
|
|
5
|
+
context and returns an AnalysisResult, so they are interchangeable.
|
|
6
|
+
"""
|
|
@@ -38,7 +38,10 @@ class ChallengeOrchestrator(Orchestrator):
|
|
|
38
38
|
if missing:
|
|
39
39
|
return AnalysisResult(error=f"challenge requires agents: {', '.join(missing)}")
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
try:
|
|
42
|
+
verdicts = agents["verifier"].run(context)
|
|
43
|
+
except Exception as exc:
|
|
44
|
+
return AnalysisResult(error=f"agent 'verifier' failed: {exc}")
|
|
42
45
|
flagged = [
|
|
43
46
|
v
|
|
44
47
|
for v in verdicts
|
|
@@ -49,7 +52,11 @@ class ChallengeOrchestrator(Orchestrator):
|
|
|
49
52
|
if not flagged:
|
|
50
53
|
return AnalysisResult(observations=verdicts)
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
try:
|
|
56
|
+
refutations = agents["refuter"].run(dataclasses.replace(context, history=flagged))
|
|
57
|
+
except Exception as exc:
|
|
58
|
+
# a refuter failure must not lose the verdicts already produced
|
|
59
|
+
return AnalysisResult(observations=verdicts, error=f"agent 'refuter' failed: {exc}")
|
|
53
60
|
reasons = {c.target: c.reason for c in refutations if isinstance(c, Concession)}
|
|
54
61
|
# the refuter targets a capability, not an individual verdict; if a capability
|
|
55
62
|
# has more than one flagged verdict the refutation is ambiguous, so leave them
|
|
@@ -11,14 +11,13 @@ from __future__ import annotations
|
|
|
11
11
|
import json
|
|
12
12
|
|
|
13
13
|
from codejury import __version__ as _tool_version
|
|
14
|
-
from codejury.domain.observation import Observation, is_problem, observation_from_dict
|
|
14
|
+
from codejury.domain.observation import _PROBLEM_STATUSES, Observation, is_problem, observation_from_dict
|
|
15
15
|
from codejury.domain.result import AnalysisResult
|
|
16
16
|
|
|
17
17
|
Results = list[tuple[str, AnalysisResult]]
|
|
18
18
|
|
|
19
19
|
_SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3, "INFO": 4}
|
|
20
20
|
_CLEARED = ("SECURE", "NOT_PRESENT")
|
|
21
|
-
_PROBLEM_STATUSES = ("VULNERABLE", "PARTIAL")
|
|
22
21
|
|
|
23
22
|
# Finding severity -> SARIF result level; VULNERABLE verdict is error, PARTIAL warning.
|
|
24
23
|
_SARIF_LEVEL = {"CRITICAL": "error", "HIGH": "error", "MEDIUM": "warning", "LOW": "note", "INFO": "note"}
|
|
@@ -15,10 +15,12 @@ from codejury.assembly import (
|
|
|
15
15
|
DEFAULT_MODEL,
|
|
16
16
|
build_orchestration,
|
|
17
17
|
make_provider,
|
|
18
|
+
orchestration_descriptor,
|
|
18
19
|
run_over_source,
|
|
19
20
|
)
|
|
20
21
|
from codejury.domain.capability import Capability
|
|
21
22
|
from codejury.domain.result import AnalysisResult
|
|
23
|
+
from codejury.infrastructure.cache import VerdictCache
|
|
22
24
|
from codejury.sources.base import Source
|
|
23
25
|
|
|
24
26
|
|
|
@@ -57,7 +59,7 @@ class Task:
|
|
|
57
59
|
|
|
58
60
|
|
|
59
61
|
def run_task(
|
|
60
|
-
task: Task, source: Source, capabilities: list[Capability]
|
|
62
|
+
task: Task, source: Source, capabilities: list[Capability], *, cache: VerdictCache | None = None
|
|
61
63
|
) -> list[tuple[str, AnalysisResult]]:
|
|
62
64
|
# api_base may come from the task as a non-secret URL; the key only from the env.
|
|
63
65
|
provider = make_provider(
|
|
@@ -69,4 +71,11 @@ def run_task(
|
|
|
69
71
|
agents, orchestrator = build_orchestration(
|
|
70
72
|
task.orchestrator, provider=provider, model=task.model, max_tokens=task.max_tokens
|
|
71
73
|
)
|
|
72
|
-
return run_over_source(
|
|
74
|
+
return run_over_source(
|
|
75
|
+
source,
|
|
76
|
+
task.select(capabilities),
|
|
77
|
+
agents,
|
|
78
|
+
orchestrator,
|
|
79
|
+
cache=cache,
|
|
80
|
+
orchestration=orchestration_descriptor(provider, task.orchestrator, task.model, task.max_tokens),
|
|
81
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codejury
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.5
|
|
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
|
|
@@ -186,7 +186,7 @@ what `--only` and a task's `capabilities:` accept:
|
|
|
186
186
|
`authn`, `authz`, `session`, `input_validation`, `output_encoding`, `crypto`,
|
|
187
187
|
`secrets`, `data_protection`, `error_logging`, `business_logic`,
|
|
188
188
|
`dependency_config`, `prompt_injection`, `insecure_output_handling`,
|
|
189
|
-
`excessive_agency`.
|
|
189
|
+
`excessive_agency`, `model_supply_chain`.
|
|
190
190
|
|
|
191
191
|
To tune for your codebase, edit these files, adding patterns or sharpening
|
|
192
192
|
wording. No code change is needed.
|
|
@@ -37,6 +37,7 @@ codejury/data/capabilities/error_logging.yaml
|
|
|
37
37
|
codejury/data/capabilities/excessive_agency.yaml
|
|
38
38
|
codejury/data/capabilities/input_validation.yaml
|
|
39
39
|
codejury/data/capabilities/insecure_output_handling.yaml
|
|
40
|
+
codejury/data/capabilities/model_supply_chain.yaml
|
|
40
41
|
codejury/data/capabilities/output_encoding.yaml
|
|
41
42
|
codejury/data/capabilities/prompt_injection.yaml
|
|
42
43
|
codejury/data/capabilities/secrets.yaml
|
|
@@ -86,6 +87,10 @@ codejury/data/golden/pi_indirect_rag_vuln.yaml
|
|
|
86
87
|
codejury/data/golden/pi_system_concat_vuln.yaml
|
|
87
88
|
codejury/data/golden/pi_user_content_concat_safe.yaml
|
|
88
89
|
codejury/data/golden/pi_user_role_safe.yaml
|
|
90
|
+
codejury/data/golden/sc_pinned_safe.yaml
|
|
91
|
+
codejury/data/golden/sc_safetensors_safe.yaml
|
|
92
|
+
codejury/data/golden/sc_torch_load_vuln.yaml
|
|
93
|
+
codejury/data/golden/sc_trust_remote_code_vuln.yaml
|
|
89
94
|
codejury/data/golden/secrets_env_safe.yaml
|
|
90
95
|
codejury/data/golden/secrets_hardcoded_vuln.yaml
|
|
91
96
|
codejury/data/golden/session_fixation_vuln.yaml
|
|
@@ -166,6 +171,7 @@ tests/test_function_source.py
|
|
|
166
171
|
tests/test_integrations.py
|
|
167
172
|
tests/test_json_parse.py
|
|
168
173
|
tests/test_litellm_provider.py
|
|
174
|
+
tests/test_openai_format.py
|
|
169
175
|
tests/test_openai_provider.py
|
|
170
176
|
tests/test_orchestrator.py
|
|
171
177
|
tests/test_parsing.py
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from types import SimpleNamespace
|
|
2
|
+
|
|
3
|
+
from codejury.providers.openai_format import choice_text
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_extracts_plain_string_content():
|
|
7
|
+
resp = SimpleNamespace(choices=[SimpleNamespace(message=SimpleNamespace(content="hello"))])
|
|
8
|
+
assert choice_text(resp) == "hello"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_extracts_content_block_list():
|
|
12
|
+
resp = SimpleNamespace(
|
|
13
|
+
choices=[SimpleNamespace(message={"content": [{"text": "a"}, {"text": "b"}, "c"]})]
|
|
14
|
+
)
|
|
15
|
+
assert choice_text(resp) == "abc"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_no_choices_returns_empty():
|
|
19
|
+
assert choice_text(SimpleNamespace(choices=[])) == ""
|
|
20
|
+
assert choice_text(SimpleNamespace(choices=None)) == ""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_none_content_returns_empty():
|
|
24
|
+
resp = SimpleNamespace(choices=[SimpleNamespace(message=SimpleNamespace(content=None))])
|
|
25
|
+
assert choice_text(resp) == ""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_dict_message_without_content():
|
|
29
|
+
resp = SimpleNamespace(choices=[SimpleNamespace(message={})])
|
|
30
|
+
assert choice_text(resp) == ""
|
|
@@ -98,3 +98,29 @@ def test_suppression_rejects_empty_match_any(tmp_path):
|
|
|
98
98
|
f.write_text("- {id: noop, path_ext: ['.py']}\n")
|
|
99
99
|
with pytest.raises(ValueError):
|
|
100
100
|
load_suppressions(f)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_challenge_surfaces_verifier_error_not_traceback():
|
|
104
|
+
# a provider failure in challenge must become AnalysisResult.error, not raise
|
|
105
|
+
from codejury.domain.artifact import CodeArtifact
|
|
106
|
+
from codejury.domain.capability import Capability
|
|
107
|
+
from codejury.domain.context import AnalysisContext
|
|
108
|
+
from codejury.orchestrators.challenge import ChallengeOrchestrator
|
|
109
|
+
|
|
110
|
+
class _Boom:
|
|
111
|
+
def run(self, ctx):
|
|
112
|
+
raise RuntimeError("provider down")
|
|
113
|
+
|
|
114
|
+
ctx = AnalysisContext(
|
|
115
|
+
artifact=CodeArtifact(kind="file", path="a.py", content="x"),
|
|
116
|
+
capabilities=[Capability(id="input_validation", name="iv")],
|
|
117
|
+
)
|
|
118
|
+
result = ChallengeOrchestrator().run({"verifier": _Boom(), "refuter": _Boom()}, ctx)
|
|
119
|
+
assert result.error and "verifier" in result.error
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_cache_get_tolerates_corrupt_entry(tmp_path):
|
|
123
|
+
from codejury.infrastructure.cache import VerdictCache
|
|
124
|
+
(tmp_path / "k.json").write_text("{ not valid json")
|
|
125
|
+
assert VerdictCache(tmp_path).get("k") is None # treated as a miss, not a crash
|
|
126
|
+
|
|
@@ -121,3 +121,15 @@ def test_worst_sink_taint_safe_sink_only_is_sanitized():
|
|
|
121
121
|
# a safe parser consuming external data is provably safe
|
|
122
122
|
code = "def f(request):\n return ast.literal_eval(request.data)\n"
|
|
123
123
|
assert worst_sink_taint(code, {"m.py": code}, VOCAB) is Taint.SANITIZED
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_method_form_propagator_keeps_receiver_taint():
|
|
127
|
+
# request.args.get("id").strip() must stay EXTERNAL, not collapse to CONSTANT
|
|
128
|
+
src = "def f(request):\n return open(request.args.get('id').strip())\n"
|
|
129
|
+
assert _taint(src, "f", "open") is Taint.EXTERNAL
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_module_function_propagator_not_polluted_by_receiver():
|
|
133
|
+
# os.path.join's receiver is a module path, not a value; taint comes from the args
|
|
134
|
+
src = "def f(request):\n return open(os.path.join(BASE, request.args['p']))\n"
|
|
135
|
+
assert _taint(src, "f", "open") is Taint.EXTERNAL # not UNKNOWN from the os.path receiver
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"""Static analysis for provenance (P1).
|
|
2
|
-
|
|
3
|
-
The code-graph / data-flow engine that gives the verifier provenance: whether a
|
|
4
|
-
value reaching a sink is attacker-controlled, sanitized, or a trusted constant.
|
|
5
|
-
This is the real fix for the taint precision floor that single-file LLM review
|
|
6
|
-
cannot reach (see ROADMAP P1). Python / AST based to start.
|
|
7
|
-
"""
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
"""codejury.orchestrators: strategies for running agents over a context.
|
|
2
|
-
|
|
3
|
-
single / debate / pipeline / reflexion. The strategy is the "any orchestration"
|
|
4
|
-
axis; a task picks one. Each takes the same agents and context and returns an
|
|
5
|
-
AnalysisResult, so they are interchangeable.
|
|
6
|
-
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codejury-0.10.3 → codejury-0.10.5}/codejury/data/capabilities/insecure_output_handling.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codejury-0.10.3 → codejury-0.10.5}/codejury/data/golden/ag_destructive_no_confirm_vuln.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|