codejury 0.14.2__tar.gz → 0.14.3__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 (80) hide show
  1. {codejury-0.14.2 → codejury-0.14.3}/PKG-INFO +1 -1
  2. {codejury-0.14.2 → codejury-0.14.3}/codejury/diff/debate.py +34 -9
  3. {codejury-0.14.2 → codejury-0.14.3}/codejury.egg-info/PKG-INFO +1 -1
  4. {codejury-0.14.2 → codejury-0.14.3}/pyproject.toml +1 -1
  5. {codejury-0.14.2 → codejury-0.14.3}/tests/test_diff_debate.py +23 -0
  6. {codejury-0.14.2 → codejury-0.14.3}/LICENSE +0 -0
  7. {codejury-0.14.2 → codejury-0.14.3}/README.md +0 -0
  8. {codejury-0.14.2 → codejury-0.14.3}/codejury/__init__.py +0 -0
  9. {codejury-0.14.2 → codejury-0.14.3}/codejury/cli.py +0 -0
  10. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/agent/full-review.md +0 -0
  11. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/agent/security-review-memory.md +0 -0
  12. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/entrypoints.yaml +0 -0
  13. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/SKILL.md +0 -0
  14. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/business-logic.md +0 -0
  15. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/code-injection.md +0 -0
  16. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/command-injection.md +0 -0
  17. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/cross-site-request-forgery.md +0 -0
  18. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/cross-site-scripting.md +0 -0
  19. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/hardcoded-secrets.md +0 -0
  20. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/http-response-splitting.md +0 -0
  21. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/improper-authentication.md +0 -0
  22. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/information-exposure.md +0 -0
  23. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/insecure-cryptography.md +0 -0
  24. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/insecure-deserialization.md +0 -0
  25. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/insecure-direct-object-reference.md +0 -0
  26. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/insecure-transport.md +0 -0
  27. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/jwt-validation.md +0 -0
  28. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/mass-assignment.md +0 -0
  29. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/missing-authorization.md +0 -0
  30. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/open-redirect.md +0 -0
  31. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/path-traversal.md +0 -0
  32. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/race-condition.md +0 -0
  33. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/replay-attack.md +0 -0
  34. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/server-side-request-forgery.md +0 -0
  35. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/server-side-template-injection.md +0 -0
  36. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/session-fixation.md +0 -0
  37. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/sql-injection.md +0 -0
  38. {codejury-0.14.2 → codejury-0.14.3}/codejury/data/rules/xml-external-entity.md +0 -0
  39. {codejury-0.14.2 → codejury-0.14.3}/codejury/diff/__init__.py +0 -0
  40. {codejury-0.14.2 → codejury-0.14.3}/codejury/diff/debate_prompts.py +0 -0
  41. {codejury-0.14.2 → codejury-0.14.3}/codejury/diff/engine.py +0 -0
  42. {codejury-0.14.2 → codejury-0.14.3}/codejury/diff/findings_filter.py +0 -0
  43. {codejury-0.14.2 → codejury-0.14.3}/codejury/diff/prompts.py +0 -0
  44. {codejury-0.14.2 → codejury-0.14.3}/codejury/diff/rules.py +0 -0
  45. {codejury-0.14.2 → codejury-0.14.3}/codejury/diff/runner.py +0 -0
  46. {codejury-0.14.2 → codejury-0.14.3}/codejury/domain/__init__.py +0 -0
  47. {codejury-0.14.2 → codejury-0.14.3}/codejury/domain/finding.py +0 -0
  48. {codejury-0.14.2 → codejury-0.14.3}/codejury/json_parse.py +0 -0
  49. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/__init__.py +0 -0
  50. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/anthropic.py +0 -0
  51. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/base.py +0 -0
  52. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/factory.py +0 -0
  53. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/litellm.py +0 -0
  54. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/mock.py +0 -0
  55. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/openai.py +0 -0
  56. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/openai_format.py +0 -0
  57. {codejury-0.14.2 → codejury-0.14.3}/codejury/providers/retry.py +0 -0
  58. {codejury-0.14.2 → codejury-0.14.3}/codejury/repo/__init__.py +0 -0
  59. {codejury-0.14.2 → codejury-0.14.3}/codejury/repo/model.py +0 -0
  60. {codejury-0.14.2 → codejury-0.14.3}/codejury/repo/scaffold.py +0 -0
  61. {codejury-0.14.2 → codejury-0.14.3}/codejury/report.py +0 -0
  62. {codejury-0.14.2 → codejury-0.14.3}/codejury/resources.py +0 -0
  63. {codejury-0.14.2 → codejury-0.14.3}/codejury.egg-info/SOURCES.txt +0 -0
  64. {codejury-0.14.2 → codejury-0.14.3}/codejury.egg-info/dependency_links.txt +0 -0
  65. {codejury-0.14.2 → codejury-0.14.3}/codejury.egg-info/entry_points.txt +0 -0
  66. {codejury-0.14.2 → codejury-0.14.3}/codejury.egg-info/requires.txt +0 -0
  67. {codejury-0.14.2 → codejury-0.14.3}/codejury.egg-info/top_level.txt +0 -0
  68. {codejury-0.14.2 → codejury-0.14.3}/setup.cfg +0 -0
  69. {codejury-0.14.2 → codejury-0.14.3}/tests/test_anthropic_provider.py +0 -0
  70. {codejury-0.14.2 → codejury-0.14.3}/tests/test_cli_audit.py +0 -0
  71. {codejury-0.14.2 → codejury-0.14.3}/tests/test_diff_engine.py +0 -0
  72. {codejury-0.14.2 → codejury-0.14.3}/tests/test_json_parse.py +0 -0
  73. {codejury-0.14.2 → codejury-0.14.3}/tests/test_litellm_provider.py +0 -0
  74. {codejury-0.14.2 → codejury-0.14.3}/tests/test_openai_format.py +0 -0
  75. {codejury-0.14.2 → codejury-0.14.3}/tests/test_openai_provider.py +0 -0
  76. {codejury-0.14.2 → codejury-0.14.3}/tests/test_repo_model.py +0 -0
  77. {codejury-0.14.2 → codejury-0.14.3}/tests/test_repo_scaffold.py +0 -0
  78. {codejury-0.14.2 → codejury-0.14.3}/tests/test_report.py +0 -0
  79. {codejury-0.14.2 → codejury-0.14.3}/tests/test_retry_provider.py +0 -0
  80. {codejury-0.14.2 → codejury-0.14.3}/tests/test_rules.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codejury
3
- Version: 0.14.2
3
+ Version: 0.14.3
4
4
  Summary: AI code security review: an adversarial diff-audit engine and an agent-driven whole-repo review methodology, with security knowledge as rich rules
5
5
  Author: AISecLabs
6
6
  License-Expression: MIT
@@ -56,6 +56,28 @@ def _dedup(items: list[dict]) -> list[dict]:
56
56
  return out
57
57
 
58
58
 
59
+ _DISMISS_VERDICTS = frozenset({"dismiss", "dismissed", "false_positive", "reject", "rejected"})
60
+
61
+
62
+ def _loc(d: dict) -> str:
63
+ line = d.get("line")
64
+ return f"{d.get('file')}:{line}" if line else str(d.get("file") or "")
65
+
66
+
67
+ def _apply_dismissals(findings: list[dict], rebuttals: list[dict]) -> list[dict]:
68
+ """Drop findings the challenger dismissed. The challenger is recall-safe (it
69
+ dismisses only when the diff shows a safe pattern: a parameterized query,
70
+ basename, an allowlist, shell=False), so honoring its dismissals is sound even
71
+ when the judge is unavailable."""
72
+ dismissed = {
73
+ str(r.get("target")) for r in rebuttals
74
+ if str(r.get("verdict", "")).strip().lower() in _DISMISS_VERDICTS
75
+ }
76
+ if not dismissed:
77
+ return findings
78
+ return [f for f in findings if _loc(f) not in dismissed and str(f.get("file") or "") not in dismissed]
79
+
80
+
59
81
  class AdversarialAuditRunner:
60
82
  def __init__(
61
83
  self,
@@ -116,16 +138,19 @@ class AdversarialAuditRunner:
116
138
  rebuttals = _dicts(challenger.get("rebuttals"))
117
139
  new_findings = _dicts(challenger.get("new_findings"))
118
140
 
119
- verdict, judge_ok = self._ask(
120
- JUDGE_SYSTEM,
121
- judge_prompt(diff, finder_findings, rebuttals, new_findings, context=context),
122
- self._judge_model,
123
- )
141
+ jp = judge_prompt(diff, finder_findings, rebuttals, new_findings, context=context)
142
+ verdict, judge_ok = self._ask(JUDGE_SYSTEM, jp, self._judge_model)
143
+ if not judge_ok:
144
+ # the judge is the filter that controls false positives, and an
145
+ # unusable reply is often transient (a flaky proxy, a blocked
146
+ # request); re-ask once before giving up on it
147
+ verdict, judge_ok = self._ask(JUDGE_SYSTEM, jp, self._judge_model)
124
148
  if not judge_ok:
125
- # the judge reply was unusable (provider error, blocked request,
126
- # prose): do not let that silently drop the candidate findings;
127
- # fall back to the unjudged finder + challenger set, flagged degraded
128
- fallback = _dedup(finder_findings + new_findings)
149
+ # judge still unusable: degrade, but apply the recall-safe
150
+ # challenger's dismissals so a transient judge outage does not pass
151
+ # through findings the challenger already showed are safe (this is
152
+ # what otherwise inflates false positives in degraded runs)
153
+ fallback = _dedup(_apply_dismissals(finder_findings, rebuttals) + new_findings)
129
154
  judged = AdversarialResult(findings=findings_from_list(fallback), rounds=rounds, degraded=True)
130
155
  degraded = True
131
156
  break
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codejury
3
- Version: 0.14.2
3
+ Version: 0.14.3
4
4
  Summary: AI code security review: an adversarial diff-audit engine and an agent-driven whole-repo review methodology, with security knowledge as rich rules
5
5
  Author: AISecLabs
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codejury"
3
- version = "0.14.2"
3
+ version = "0.14.3"
4
4
  description = "AI code security review: an adversarial diff-audit engine and an agent-driven whole-repo review methodology, with security knowledge as rich rules"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -156,6 +156,29 @@ def test_provider_exception_degrades_rather_than_crashes():
156
156
  assert out.degraded is True
157
157
 
158
158
 
159
+ def test_judge_retry_recovers_from_a_transient_unusable_reply():
160
+ # the judge's first reply is unusable, the retry succeeds: not degraded,
161
+ # and the judge verdict is applied normally
162
+ _, out = _run([_finder([_VULN]), _challenger(), "blocked by waf", _judge([_VULN])], max_rounds=1)
163
+ assert out.degraded is False
164
+ assert [f.category for f in out.findings] == ["sql_injection"]
165
+
166
+
167
+ def test_degraded_fallback_drops_challenger_dismissed_findings():
168
+ # judge stays unusable (both the call and its retry); the degraded fallback
169
+ # must still honor the challenger's recall-safe dismissals rather than pass
170
+ # every finder finding through, which is what inflates false positives
171
+ second = {**_VULN, "line": 5, "category": "xss"}
172
+ _, out = _run(
173
+ [_finder([_VULN, second]),
174
+ _challenger(rebuttals=[{"target": "app.py:5", "verdict": "dismiss", "reason": "output is escaped"}]),
175
+ "blocked", "blocked"], # judge call + its one retry both unusable
176
+ max_rounds=1,
177
+ )
178
+ assert out.degraded is True
179
+ assert [f.category for f in out.findings] == ["sql_injection"] # the dismissed xss at app.py:5 is dropped
180
+
181
+
159
182
  def test_per_role_models_are_used():
160
183
  provider = MockProvider(responses=[_finder([]), _challenger(), _judge([])], default="{}")
161
184
  AdversarialAuditRunner(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes