codejury 0.10.4__tar.gz → 0.10.6__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.
Files changed (211) hide show
  1. {codejury-0.10.4 → codejury-0.10.6}/PKG-INFO +1 -1
  2. codejury-0.10.6/codejury/analysis/__init__.py +8 -0
  3. {codejury-0.10.4 → codejury-0.10.6}/codejury/analysis/taint.py +27 -10
  4. {codejury-0.10.4 → codejury-0.10.6}/codejury/cli.py +5 -1
  5. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ag_arbitrary_tool_vuln.yaml +1 -0
  6. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/authn_sha256_password.yaml +1 -0
  7. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/authz_idor_vuln.yaml +1 -0
  8. codejury-0.10.6/codejury/data/golden/business_logic_atomic_safe.yaml +15 -0
  9. codejury-0.10.6/codejury/data/golden/business_logic_toctou_vuln.yaml +14 -0
  10. codejury-0.10.6/codejury/data/golden/crypto_aesgcm_nonce_safe.yaml +13 -0
  11. codejury-0.10.6/codejury/data/golden/crypto_static_iv_vuln.yaml +14 -0
  12. codejury-0.10.6/codejury/data/golden/data_protection_cleartext_http_vuln.yaml +11 -0
  13. codejury-0.10.6/codejury/data/golden/data_protection_encrypted_at_rest_safe.yaml +11 -0
  14. codejury-0.10.6/codejury/data/golden/dependency_config_debug_off_safe.yaml +12 -0
  15. codejury-0.10.6/codejury/data/golden/dependency_config_debug_true_vuln.yaml +11 -0
  16. codejury-0.10.6/codejury/data/golden/error_logging_reraise_safe.yaml +15 -0
  17. codejury-0.10.6/codejury/data/golden/error_logging_swallow_vuln.yaml +14 -0
  18. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ioh_exec_output_vuln.yaml +1 -0
  19. codejury-0.10.6/codejury/data/golden/output_encoding_header_injection_vuln.yaml +11 -0
  20. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/pi_system_concat_vuln.yaml +1 -0
  21. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sc_trust_remote_code_vuln.yaml +1 -0
  22. codejury-0.10.6/codejury/data/golden/secrets_from_config_safe.yaml +12 -0
  23. codejury-0.10.6/codejury/data/golden/secrets_hardcoded_token_vuln.yaml +13 -0
  24. codejury-0.10.6/codejury/data/golden/session_cookie_flags_safe.yaml +10 -0
  25. codejury-0.10.6/codejury/data/golden/session_insecure_cookie_vuln.yaml +11 -0
  26. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/xss_innerhtml_vuln.yaml +1 -0
  27. codejury-0.10.6/codejury/infrastructure/__init__.py +4 -0
  28. {codejury-0.10.4 → codejury-0.10.6}/codejury/infrastructure/cache.py +17 -3
  29. codejury-0.10.6/codejury/orchestrators/__init__.py +6 -0
  30. {codejury-0.10.4 → codejury-0.10.6}/codejury/orchestrators/challenge.py +9 -2
  31. {codejury-0.10.4 → codejury-0.10.6}/codejury/reporting.py +1 -2
  32. {codejury-0.10.4 → codejury-0.10.6}/codejury/tasks/base.py +11 -2
  33. {codejury-0.10.4 → codejury-0.10.6}/codejury.egg-info/PKG-INFO +1 -1
  34. {codejury-0.10.4 → codejury-0.10.6}/codejury.egg-info/SOURCES.txt +16 -0
  35. {codejury-0.10.4 → codejury-0.10.6}/pyproject.toml +1 -1
  36. codejury-0.10.6/tests/test_openai_format.py +30 -0
  37. {codejury-0.10.4 → codejury-0.10.6}/tests/test_review_fixes.py +26 -0
  38. {codejury-0.10.4 → codejury-0.10.6}/tests/test_taint.py +12 -0
  39. codejury-0.10.4/codejury/analysis/__init__.py +0 -7
  40. codejury-0.10.4/codejury/infrastructure/__init__.py +0 -4
  41. codejury-0.10.4/codejury/orchestrators/__init__.py +0 -6
  42. {codejury-0.10.4 → codejury-0.10.6}/LICENSE +0 -0
  43. {codejury-0.10.4 → codejury-0.10.6}/README.md +0 -0
  44. {codejury-0.10.4 → codejury-0.10.6}/codejury/__init__.py +0 -0
  45. {codejury-0.10.4 → codejury-0.10.6}/codejury/agents/__init__.py +0 -0
  46. {codejury-0.10.4 → codejury-0.10.6}/codejury/agents/base.py +0 -0
  47. {codejury-0.10.4 → codejury-0.10.6}/codejury/agents/debate.py +0 -0
  48. {codejury-0.10.4 → codejury-0.10.6}/codejury/agents/mock.py +0 -0
  49. {codejury-0.10.4 → codejury-0.10.6}/codejury/agents/parsing.py +0 -0
  50. {codejury-0.10.4 → codejury-0.10.6}/codejury/agents/refuter.py +0 -0
  51. {codejury-0.10.4 → codejury-0.10.6}/codejury/agents/verifier.py +0 -0
  52. {codejury-0.10.4 → codejury-0.10.6}/codejury/analysis/provenance.py +0 -0
  53. {codejury-0.10.4 → codejury-0.10.6}/codejury/assembly.py +0 -0
  54. {codejury-0.10.4 → codejury-0.10.6}/codejury/baseline.py +0 -0
  55. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/authentication.yaml +0 -0
  56. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/authorization.yaml +0 -0
  57. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/business_logic.yaml +0 -0
  58. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/crypto.yaml +0 -0
  59. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/data_protection.yaml +0 -0
  60. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/dependency_config.yaml +0 -0
  61. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/error_logging.yaml +0 -0
  62. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/excessive_agency.yaml +0 -0
  63. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/input_validation.yaml +0 -0
  64. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/insecure_output_handling.yaml +0 -0
  65. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/model_supply_chain.yaml +0 -0
  66. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/output_encoding.yaml +0 -0
  67. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/prompt_injection.yaml +0 -0
  68. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/secrets.yaml +0 -0
  69. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/capabilities/session.yaml +0 -0
  70. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ag_allowlist_safe.yaml +0 -0
  71. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ag_destructive_no_confirm_vuln.yaml +0 -0
  72. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ag_fixed_enum_safe.yaml +0 -0
  73. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ag_human_approval_safe.yaml +0 -0
  74. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ag_model_confirmed_vuln.yaml +0 -0
  75. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/authn_bcrypt_password.yaml +0 -0
  76. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/authn_jwt_noverify_vuln.yaml +0 -0
  77. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/authn_jwt_verified_safe.yaml +0 -0
  78. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/authn_sha256_checksum_safe.yaml +0 -0
  79. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/authn_weak_hash_indirect_vuln.yaml +0 -0
  80. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/authz_owner_safe.yaml +0 -0
  81. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/business_logic_price_tamper_vuln.yaml +0 -0
  82. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/business_logic_server_checked_safe.yaml +0 -0
  83. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/cmdi_fixed_argv_safe.yaml +0 -0
  84. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/cmdi_ossystem_vuln.yaml +0 -0
  85. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/cmdi_subprocess_safe.yaml +0 -0
  86. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/crypto_aesgcm_safe.yaml +0 -0
  87. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/crypto_ecb_vuln.yaml +0 -0
  88. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/data_protection_plaintext_pii_vuln.yaml +0 -0
  89. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/data_protection_tokenized_safe.yaml +0 -0
  90. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/dependency_config_tls_verify_off_vuln.yaml +0 -0
  91. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/dependency_config_tls_verify_on_safe.yaml +0 -0
  92. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/deserialize_json_safe.yaml +0 -0
  93. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/deserialize_pickle_vuln.yaml +0 -0
  94. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/error_logging_redacted_safe.yaml +0 -0
  95. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/error_logging_secret_leak_vuln.yaml +0 -0
  96. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ioh_escaped_output_safe.yaml +0 -0
  97. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ioh_innerhtml_output_vuln.yaml +0 -0
  98. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ioh_json_response_safe.yaml +0 -0
  99. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ioh_output_to_sql_vuln.yaml +0 -0
  100. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ioh_schema_validated_safe.yaml +0 -0
  101. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/literal_eval_safe.yaml +0 -0
  102. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/path_basename_safe.yaml +0 -0
  103. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/path_contained_safe.yaml +0 -0
  104. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/path_traversal_vuln.yaml +0 -0
  105. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/pi_delimited_data_safe.yaml +0 -0
  106. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/pi_format_role_vuln.yaml +0 -0
  107. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/pi_indirect_rag_vuln.yaml +0 -0
  108. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/pi_user_content_concat_safe.yaml +0 -0
  109. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/pi_user_role_safe.yaml +0 -0
  110. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sc_pinned_safe.yaml +0 -0
  111. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sc_safetensors_safe.yaml +0 -0
  112. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sc_torch_load_vuln.yaml +0 -0
  113. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/secrets_env_safe.yaml +0 -0
  114. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/secrets_hardcoded_vuln.yaml +0 -0
  115. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/session_fixation_vuln.yaml +0 -0
  116. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/session_secure_cookie_safe.yaml +0 -0
  117. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sql_constant_concat_safe.yaml +0 -0
  118. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sqli_format_vuln.yaml +0 -0
  119. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sqli_fstring_query.yaml +0 -0
  120. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sqli_indirect_var_vuln.yaml +0 -0
  121. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/sqli_parameterized_query.yaml +0 -0
  122. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ssrf_allowlist_safe.yaml +0 -0
  123. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ssrf_constant_url_safe.yaml +0 -0
  124. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ssrf_substring_allowlist_bypass_vuln.yaml +0 -0
  125. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/ssrf_user_url_vuln.yaml +0 -0
  126. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/xfile_idor_no_check_vuln.yaml +0 -0
  127. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/xfile_idor_owner_checked_safe.yaml +0 -0
  128. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/xfile_path_sanitized_safe.yaml +0 -0
  129. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/xfile_path_tainted_vuln.yaml +0 -0
  130. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/xss_innerhtml_constant_safe.yaml +0 -0
  131. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/golden/xss_textcontent_safe.yaml +0 -0
  132. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/suppressions.yaml +0 -0
  133. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/taint.yaml +0 -0
  134. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/tasks/audit_diff_debate.yaml +0 -0
  135. {codejury-0.10.4 → codejury-0.10.6}/codejury/data/tasks/quick_scan_single.yaml +0 -0
  136. {codejury-0.10.4 → codejury-0.10.6}/codejury/domain/__init__.py +0 -0
  137. {codejury-0.10.4 → codejury-0.10.6}/codejury/domain/artifact.py +0 -0
  138. {codejury-0.10.4 → codejury-0.10.6}/codejury/domain/capability.py +0 -0
  139. {codejury-0.10.4 → codejury-0.10.6}/codejury/domain/context.py +0 -0
  140. {codejury-0.10.4 → codejury-0.10.6}/codejury/domain/observation.py +0 -0
  141. {codejury-0.10.4 → codejury-0.10.6}/codejury/domain/result.py +0 -0
  142. {codejury-0.10.4 → codejury-0.10.6}/codejury/evaluation.py +0 -0
  143. {codejury-0.10.4 → codejury-0.10.6}/codejury/infrastructure/json_parse.py +0 -0
  144. {codejury-0.10.4 → codejury-0.10.6}/codejury/integrations/__init__.py +0 -0
  145. {codejury-0.10.4 → codejury-0.10.6}/codejury/integrations/github.py +0 -0
  146. {codejury-0.10.4 → codejury-0.10.6}/codejury/orchestrators/adaptive.py +0 -0
  147. {codejury-0.10.4 → codejury-0.10.6}/codejury/orchestrators/base.py +0 -0
  148. {codejury-0.10.4 → codejury-0.10.6}/codejury/orchestrators/debate.py +0 -0
  149. {codejury-0.10.4 → codejury-0.10.6}/codejury/orchestrators/pipeline.py +0 -0
  150. {codejury-0.10.4 → codejury-0.10.6}/codejury/orchestrators/reflexion.py +0 -0
  151. {codejury-0.10.4 → codejury-0.10.6}/codejury/orchestrators/single.py +0 -0
  152. {codejury-0.10.4 → codejury-0.10.6}/codejury/orchestrators/taint_gate.py +0 -0
  153. {codejury-0.10.4 → codejury-0.10.6}/codejury/providers/__init__.py +0 -0
  154. {codejury-0.10.4 → codejury-0.10.6}/codejury/providers/anthropic.py +0 -0
  155. {codejury-0.10.4 → codejury-0.10.6}/codejury/providers/base.py +0 -0
  156. {codejury-0.10.4 → codejury-0.10.6}/codejury/providers/litellm.py +0 -0
  157. {codejury-0.10.4 → codejury-0.10.6}/codejury/providers/mock.py +0 -0
  158. {codejury-0.10.4 → codejury-0.10.6}/codejury/providers/openai.py +0 -0
  159. {codejury-0.10.4 → codejury-0.10.6}/codejury/providers/openai_format.py +0 -0
  160. {codejury-0.10.4 → codejury-0.10.6}/codejury/providers/retry.py +0 -0
  161. {codejury-0.10.4 → codejury-0.10.6}/codejury/resources.py +0 -0
  162. {codejury-0.10.4 → codejury-0.10.6}/codejury/sources/__init__.py +0 -0
  163. {codejury-0.10.4 → codejury-0.10.6}/codejury/sources/base.py +0 -0
  164. {codejury-0.10.4 → codejury-0.10.6}/codejury/sources/callers.py +0 -0
  165. {codejury-0.10.4 → codejury-0.10.6}/codejury/sources/chunker.py +0 -0
  166. {codejury-0.10.4 → codejury-0.10.6}/codejury/sources/diff.py +0 -0
  167. {codejury-0.10.4 → codejury-0.10.6}/codejury/sources/function.py +0 -0
  168. {codejury-0.10.4 → codejury-0.10.6}/codejury/sources/mock.py +0 -0
  169. {codejury-0.10.4 → codejury-0.10.6}/codejury/sources/repo.py +0 -0
  170. {codejury-0.10.4 → codejury-0.10.6}/codejury/suppression.py +0 -0
  171. {codejury-0.10.4 → codejury-0.10.6}/codejury/tasks/__init__.py +0 -0
  172. {codejury-0.10.4 → codejury-0.10.6}/codejury/tasks/registry.py +0 -0
  173. {codejury-0.10.4 → codejury-0.10.6}/codejury.egg-info/dependency_links.txt +0 -0
  174. {codejury-0.10.4 → codejury-0.10.6}/codejury.egg-info/entry_points.txt +0 -0
  175. {codejury-0.10.4 → codejury-0.10.6}/codejury.egg-info/requires.txt +0 -0
  176. {codejury-0.10.4 → codejury-0.10.6}/codejury.egg-info/top_level.txt +0 -0
  177. {codejury-0.10.4 → codejury-0.10.6}/setup.cfg +0 -0
  178. {codejury-0.10.4 → codejury-0.10.6}/tests/test_adaptive.py +0 -0
  179. {codejury-0.10.4 → codejury-0.10.6}/tests/test_anthropic_provider.py +0 -0
  180. {codejury-0.10.4 → codejury-0.10.6}/tests/test_assembly.py +0 -0
  181. {codejury-0.10.4 → codejury-0.10.6}/tests/test_audit_pipeline.py +0 -0
  182. {codejury-0.10.4 → codejury-0.10.6}/tests/test_baseline.py +0 -0
  183. {codejury-0.10.4 → codejury-0.10.6}/tests/test_cache.py +0 -0
  184. {codejury-0.10.4 → codejury-0.10.6}/tests/test_callers.py +0 -0
  185. {codejury-0.10.4 → codejury-0.10.6}/tests/test_capability.py +0 -0
  186. {codejury-0.10.4 → codejury-0.10.6}/tests/test_challenge.py +0 -0
  187. {codejury-0.10.4 → codejury-0.10.6}/tests/test_cli_audit.py +0 -0
  188. {codejury-0.10.4 → codejury-0.10.6}/tests/test_context.py +0 -0
  189. {codejury-0.10.4 → codejury-0.10.6}/tests/test_debate_agents.py +0 -0
  190. {codejury-0.10.4 → codejury-0.10.6}/tests/test_debate_orchestrator.py +0 -0
  191. {codejury-0.10.4 → codejury-0.10.6}/tests/test_diff_source.py +0 -0
  192. {codejury-0.10.4 → codejury-0.10.6}/tests/test_evaluation.py +0 -0
  193. {codejury-0.10.4 → codejury-0.10.6}/tests/test_function_source.py +0 -0
  194. {codejury-0.10.4 → codejury-0.10.6}/tests/test_integrations.py +0 -0
  195. {codejury-0.10.4 → codejury-0.10.6}/tests/test_json_parse.py +0 -0
  196. {codejury-0.10.4 → codejury-0.10.6}/tests/test_litellm_provider.py +0 -0
  197. {codejury-0.10.4 → codejury-0.10.6}/tests/test_openai_provider.py +0 -0
  198. {codejury-0.10.4 → codejury-0.10.6}/tests/test_orchestrator.py +0 -0
  199. {codejury-0.10.4 → codejury-0.10.6}/tests/test_parsing.py +0 -0
  200. {codejury-0.10.4 → codejury-0.10.6}/tests/test_pipeline_orchestrator.py +0 -0
  201. {codejury-0.10.4 → codejury-0.10.6}/tests/test_provenance.py +0 -0
  202. {codejury-0.10.4 → codejury-0.10.6}/tests/test_reflexion_orchestrator.py +0 -0
  203. {codejury-0.10.4 → codejury-0.10.6}/tests/test_repo_source.py +0 -0
  204. {codejury-0.10.4 → codejury-0.10.6}/tests/test_reporting.py +0 -0
  205. {codejury-0.10.4 → codejury-0.10.6}/tests/test_retry_provider.py +0 -0
  206. {codejury-0.10.4 → codejury-0.10.6}/tests/test_sarif.py +0 -0
  207. {codejury-0.10.4 → codejury-0.10.6}/tests/test_suppression.py +0 -0
  208. {codejury-0.10.4 → codejury-0.10.6}/tests/test_taint_crossfile.py +0 -0
  209. {codejury-0.10.4 → codejury-0.10.6}/tests/test_taint_gate.py +0 -0
  210. {codejury-0.10.4 → codejury-0.10.6}/tests/test_tasks.py +0 -0
  211. {codejury-0.10.4 → codejury-0.10.6}/tests/test_verifier.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codejury
3
- Version: 0.10.4
3
+ Version: 0.10.6
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
@@ -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
- return _combine([recurse(a) for a in node.args])
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
- funcs = [n for n in ast.walk(tree) if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))]
253
+ scopes = _scope_index(tree)
246
254
  for call in find_calls(tree, name):
247
- sites.append((_enclosing_scope(funcs, call) or tree, call))
255
+ sites.append((scopes.get(id(call), tree), call))
248
256
  return sites
249
257
 
250
258
 
251
- def _enclosing_scope(funcs: list[ast.AST], call: ast.Call) -> ast.AST | None:
252
- containing = [f for f in funcs if any(node is call for node in ast.walk(f))]
253
- if not containing:
254
- return None # module-level call site
255
- return min(containing, key=lambda f: sum(1 for _ in ast.walk(f))) # innermost
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
- funcs = [n for n in ast.walk(tree) if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))]
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 = _enclosing_scope(funcs, call) or tree
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], DiffSource(_read_diff(args.diff)), load_capabilities(args.capabilities)
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))
@@ -1,4 +1,5 @@
1
1
  capability: excessive_agency
2
+ split: held-out
2
3
  vulnerable: true
3
4
  expected_verdict: VULNERABLE
4
5
  cwe: CWE-862
@@ -1,4 +1,5 @@
1
1
  capability: authn
2
+ split: held-out
2
3
  vulnerable: true
3
4
  code: |
4
5
  def store_password(pwd: str) -> str:
@@ -1,4 +1,5 @@
1
1
  capability: authz
2
+ split: held-out
2
3
  vulnerable: true
3
4
  code: |
4
5
  def get_invoice(request):
@@ -0,0 +1,15 @@
1
+ capability: business_logic
2
+ vulnerable: false
3
+ expected_verdict: SECURE
4
+ cwe: ""
5
+ source: synthetic
6
+ notes: >
7
+ A single atomic conditional update debits only when the balance suffices, so
8
+ there is no check-then-act race.
9
+ code: |
10
+ def withdraw(account, amount):
11
+ result = db.execute(
12
+ "UPDATE accounts SET balance = balance - %s WHERE id = %s AND balance >= %s",
13
+ (amount, account.id, amount),
14
+ )
15
+ return result.rowcount == 1
@@ -0,0 +1,14 @@
1
+ capability: business_logic
2
+ vulnerable: true
3
+ expected_verdict: VULNERABLE
4
+ cwe: CWE-367
5
+ source: synthetic
6
+ notes: >
7
+ The balance is checked and then debited without a lock or atomic update, so two
8
+ concurrent withdrawals both pass the check and overdraw the account.
9
+ code: |
10
+ def withdraw(account, amount):
11
+ if account.balance >= amount:
12
+ account.balance -= amount
13
+ return True
14
+ return False
@@ -0,0 +1,13 @@
1
+ capability: crypto
2
+ vulnerable: false
3
+ expected_verdict: SECURE
4
+ cwe: ""
5
+ source: synthetic
6
+ split: held-out
7
+ notes: >
8
+ Authenticated encryption (AES-GCM) with a fresh random nonce per message. No
9
+ fixed IV, no unauthenticated mode.
10
+ code: |
11
+ def encrypt(data, key):
12
+ nonce = os.urandom(12)
13
+ return nonce + AESGCM(key).encrypt(nonce, data, None)
@@ -0,0 +1,14 @@
1
+ capability: crypto
2
+ vulnerable: true
3
+ expected_verdict: VULNERABLE
4
+ cwe: CWE-329
5
+ source: synthetic
6
+ notes: >
7
+ A fixed all-zero IV with CBC makes encryption deterministic, so identical
8
+ plaintexts produce identical ciphertexts and patterns leak. The IV must be
9
+ random per message.
10
+ code: |
11
+ def encrypt(data, key):
12
+ iv = b"\x00" * 16
13
+ cipher = AES.new(key, AES.MODE_CBC, iv)
14
+ return cipher.encrypt(pad(data))
@@ -0,0 +1,11 @@
1
+ capability: data_protection
2
+ vulnerable: true
3
+ expected_verdict: VULNERABLE
4
+ cwe: CWE-319
5
+ source: synthetic
6
+ notes: >
7
+ PII is posted over plaintext http, so it can be read by anyone on the network
8
+ path. It must travel over TLS.
9
+ code: |
10
+ def sync(user):
11
+ requests.post("http://crm.internal/contacts", json={"ssn": user.ssn, "email": user.email})
@@ -0,0 +1,11 @@
1
+ capability: data_protection
2
+ vulnerable: false
3
+ expected_verdict: SECURE
4
+ cwe: ""
5
+ source: synthetic
6
+ notes: >
7
+ The SSN is encrypted before it is stored, so no plaintext PII is persisted.
8
+ code: |
9
+ def save(user):
10
+ ciphertext = AESGCM(KEY).encrypt(os.urandom(12), user.ssn.encode(), None)
11
+ db.store(user.id, ciphertext)
@@ -0,0 +1,12 @@
1
+ capability: dependency_config
2
+ vulnerable: false
3
+ expected_verdict: SECURE
4
+ cwe: ""
5
+ source: synthetic
6
+ split: held-out
7
+ notes: >
8
+ An ordinary run with the debugger left at its safe default. A normal app.run
9
+ must not be flagged.
10
+ code: |
11
+ def main():
12
+ app.run(host="127.0.0.1")
@@ -0,0 +1,11 @@
1
+ capability: dependency_config
2
+ vulnerable: true
3
+ expected_verdict: VULNERABLE
4
+ cwe: CWE-489
5
+ source: synthetic
6
+ notes: >
7
+ Running with the debugger enabled in production exposes an interactive console
8
+ that can execute code on the server.
9
+ code: |
10
+ def main():
11
+ app.run(host="0.0.0.0", debug=True)
@@ -0,0 +1,15 @@
1
+ capability: error_logging
2
+ vulnerable: false
3
+ expected_verdict: SECURE
4
+ cwe: ""
5
+ source: synthetic
6
+ split: held-out
7
+ notes: >
8
+ The error is logged and re-raised, so the failure is recorded and not hidden.
9
+ code: |
10
+ def charge(card):
11
+ try:
12
+ gateway.charge(card)
13
+ except PaymentError:
14
+ logger.exception("charge failed")
15
+ raise
@@ -0,0 +1,14 @@
1
+ capability: error_logging
2
+ vulnerable: true
3
+ expected_verdict: VULNERABLE
4
+ cwe: CWE-390
5
+ source: synthetic
6
+ notes: >
7
+ The bare except discards every error, including security-relevant failures, so
8
+ a failed charge looks like a success and nothing is logged.
9
+ code: |
10
+ def charge(card):
11
+ try:
12
+ gateway.charge(card)
13
+ except Exception:
14
+ pass
@@ -1,4 +1,5 @@
1
1
  capability: insecure_output_handling
2
+ split: held-out
2
3
  vulnerable: true
3
4
  expected_verdict: VULNERABLE
4
5
  cwe: CWE-94
@@ -0,0 +1,11 @@
1
+ capability: output_encoding
2
+ vulnerable: true
3
+ expected_verdict: VULNERABLE
4
+ cwe: CWE-113
5
+ source: synthetic
6
+ notes: >
7
+ User input is concatenated into a response header without stripping CR or LF, so
8
+ a crafted value can inject extra headers or split the response.
9
+ code: |
10
+ def download(response, filename):
11
+ response.headers["Content-Disposition"] = "attachment; filename=" + filename
@@ -1,4 +1,5 @@
1
1
  capability: prompt_injection
2
+ split: held-out
2
3
  vulnerable: true
3
4
  expected_verdict: VULNERABLE
4
5
  cwe: CWE-1427
@@ -1,4 +1,5 @@
1
1
  capability: model_supply_chain
2
+ split: held-out
2
3
  vulnerable: true
3
4
  expected_verdict: VULNERABLE
4
5
  cwe: CWE-494
@@ -0,0 +1,12 @@
1
+ capability: secrets
2
+ vulnerable: false
3
+ expected_verdict: SECURE
4
+ cwe: ""
5
+ source: synthetic
6
+ split: held-out
7
+ notes: >
8
+ The token is read from settings at runtime, not a literal. A variable named
9
+ token that holds a config lookup must not be flagged.
10
+ code: |
11
+ def client():
12
+ return Service(token=settings.API_TOKEN)
@@ -0,0 +1,13 @@
1
+ capability: secrets
2
+ vulnerable: true
3
+ expected_verdict: VULNERABLE
4
+ cwe: CWE-798
5
+ source: synthetic
6
+ notes: >
7
+ A credential literal committed in source is exposed to everyone with repo
8
+ access and lives forever in version history. It belongs in the environment.
9
+ code: |
10
+ API_TOKEN = "9c1185a5c5e9fc54612808977ee8f548b2258d31"
11
+
12
+ def client():
13
+ return Service(token=API_TOKEN)
@@ -0,0 +1,10 @@
1
+ capability: session
2
+ vulnerable: false
3
+ expected_verdict: SECURE
4
+ cwe: ""
5
+ source: synthetic
6
+ notes: >
7
+ The session cookie sets Secure, HttpOnly, and SameSite.
8
+ code: |
9
+ def set_session(response, sid):
10
+ response.set_cookie("session", sid, secure=True, httponly=True, samesite="Lax")
@@ -0,0 +1,11 @@
1
+ capability: session
2
+ vulnerable: true
3
+ expected_verdict: VULNERABLE
4
+ cwe: CWE-614
5
+ source: synthetic
6
+ notes: >
7
+ The session cookie is set without Secure, HttpOnly, or SameSite, so it is
8
+ readable by JavaScript and sent over plaintext.
9
+ code: |
10
+ def set_session(response, sid):
11
+ response.set_cookie("session", sid)
@@ -1,4 +1,5 @@
1
1
  capability: output_encoding
2
+ split: held-out
2
3
  vulnerable: true
3
4
  code: |
4
5
  function render(name) {
@@ -0,0 +1,4 @@
1
+ """codejury.infrastructure: Layer 1 cross-cutting utilities, json parsing and the verdict cache.
2
+
3
+ Lowest layer: depends on nothing else in codejury, used by the layers above.
4
+ """
@@ -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
- with open(path, encoding="utf-8") as f:
69
- return AnalysisResult.from_dict(json.load(f))
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
- with open(self._dir / f"{key}.json", "w", encoding="utf-8") as f:
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
- verdicts = agents["verifier"].run(context)
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
- refutations = agents["refuter"].run(dataclasses.replace(context, history=flagged))
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(source, task.select(capabilities), agents, orchestrator)
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.4
3
+ Version: 0.10.6
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
@@ -56,21 +56,31 @@ codejury/data/golden/authn_sha256_password.yaml
56
56
  codejury/data/golden/authn_weak_hash_indirect_vuln.yaml
57
57
  codejury/data/golden/authz_idor_vuln.yaml
58
58
  codejury/data/golden/authz_owner_safe.yaml
59
+ codejury/data/golden/business_logic_atomic_safe.yaml
59
60
  codejury/data/golden/business_logic_price_tamper_vuln.yaml
60
61
  codejury/data/golden/business_logic_server_checked_safe.yaml
62
+ codejury/data/golden/business_logic_toctou_vuln.yaml
61
63
  codejury/data/golden/cmdi_fixed_argv_safe.yaml
62
64
  codejury/data/golden/cmdi_ossystem_vuln.yaml
63
65
  codejury/data/golden/cmdi_subprocess_safe.yaml
66
+ codejury/data/golden/crypto_aesgcm_nonce_safe.yaml
64
67
  codejury/data/golden/crypto_aesgcm_safe.yaml
65
68
  codejury/data/golden/crypto_ecb_vuln.yaml
69
+ codejury/data/golden/crypto_static_iv_vuln.yaml
70
+ codejury/data/golden/data_protection_cleartext_http_vuln.yaml
71
+ codejury/data/golden/data_protection_encrypted_at_rest_safe.yaml
66
72
  codejury/data/golden/data_protection_plaintext_pii_vuln.yaml
67
73
  codejury/data/golden/data_protection_tokenized_safe.yaml
74
+ codejury/data/golden/dependency_config_debug_off_safe.yaml
75
+ codejury/data/golden/dependency_config_debug_true_vuln.yaml
68
76
  codejury/data/golden/dependency_config_tls_verify_off_vuln.yaml
69
77
  codejury/data/golden/dependency_config_tls_verify_on_safe.yaml
70
78
  codejury/data/golden/deserialize_json_safe.yaml
71
79
  codejury/data/golden/deserialize_pickle_vuln.yaml
72
80
  codejury/data/golden/error_logging_redacted_safe.yaml
81
+ codejury/data/golden/error_logging_reraise_safe.yaml
73
82
  codejury/data/golden/error_logging_secret_leak_vuln.yaml
83
+ codejury/data/golden/error_logging_swallow_vuln.yaml
74
84
  codejury/data/golden/ioh_escaped_output_safe.yaml
75
85
  codejury/data/golden/ioh_exec_output_vuln.yaml
76
86
  codejury/data/golden/ioh_innerhtml_output_vuln.yaml
@@ -78,6 +88,7 @@ codejury/data/golden/ioh_json_response_safe.yaml
78
88
  codejury/data/golden/ioh_output_to_sql_vuln.yaml
79
89
  codejury/data/golden/ioh_schema_validated_safe.yaml
80
90
  codejury/data/golden/literal_eval_safe.yaml
91
+ codejury/data/golden/output_encoding_header_injection_vuln.yaml
81
92
  codejury/data/golden/path_basename_safe.yaml
82
93
  codejury/data/golden/path_contained_safe.yaml
83
94
  codejury/data/golden/path_traversal_vuln.yaml
@@ -92,8 +103,12 @@ codejury/data/golden/sc_safetensors_safe.yaml
92
103
  codejury/data/golden/sc_torch_load_vuln.yaml
93
104
  codejury/data/golden/sc_trust_remote_code_vuln.yaml
94
105
  codejury/data/golden/secrets_env_safe.yaml
106
+ codejury/data/golden/secrets_from_config_safe.yaml
107
+ codejury/data/golden/secrets_hardcoded_token_vuln.yaml
95
108
  codejury/data/golden/secrets_hardcoded_vuln.yaml
109
+ codejury/data/golden/session_cookie_flags_safe.yaml
96
110
  codejury/data/golden/session_fixation_vuln.yaml
111
+ codejury/data/golden/session_insecure_cookie_vuln.yaml
97
112
  codejury/data/golden/session_secure_cookie_safe.yaml
98
113
  codejury/data/golden/sql_constant_concat_safe.yaml
99
114
  codejury/data/golden/sqli_format_vuln.yaml
@@ -171,6 +186,7 @@ tests/test_function_source.py
171
186
  tests/test_integrations.py
172
187
  tests/test_json_parse.py
173
188
  tests/test_litellm_provider.py
189
+ tests/test_openai_format.py
174
190
  tests/test_openai_provider.py
175
191
  tests/test_orchestrator.py
176
192
  tests/test_parsing.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codejury"
3
- version = "0.10.4"
3
+ version = "0.10.6"
4
4
  description = "General-purpose Application Security AI audit framework: five-layer architecture, capabilities as first-class data"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -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) == ""