sourcecode 1.35.25__py3-none-any.whl → 1.35.26__py3-none-any.whl
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.
- sourcecode/__init__.py +1 -1
- sourcecode/migrate_check.py +40 -1
- sourcecode/repository_ir.py +52 -1
- sourcecode/spring_event_topology.py +20 -1
- {sourcecode-1.35.25.dist-info → sourcecode-1.35.26.dist-info}/METADATA +3 -3
- {sourcecode-1.35.25.dist-info → sourcecode-1.35.26.dist-info}/RECORD +9 -9
- {sourcecode-1.35.25.dist-info → sourcecode-1.35.26.dist-info}/WHEEL +0 -0
- {sourcecode-1.35.25.dist-info → sourcecode-1.35.26.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.35.25.dist-info → sourcecode-1.35.26.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/migrate_check.py
CHANGED
|
@@ -868,9 +868,34 @@ def _find_build_files(root: Path) -> list[tuple[Path, str]]:
|
|
|
868
868
|
return results
|
|
869
869
|
|
|
870
870
|
|
|
871
|
+
def _resolve_maven_properties(text: str) -> str:
|
|
872
|
+
"""Substitute ${prop} references with values from the <properties> block.
|
|
873
|
+
|
|
874
|
+
Handles single-level property references that appear in the same pom.xml.
|
|
875
|
+
Multi-level references (${a} where a=${b}) are resolved up to 3 passes.
|
|
876
|
+
"""
|
|
877
|
+
props: dict[str, str] = {}
|
|
878
|
+
for m in re.finditer(r'<([A-Za-z][\w.\-]*)>\s*([^<${}]+?)\s*</\1>', text):
|
|
879
|
+
props[m.group(1)] = m.group(2).strip()
|
|
880
|
+
if not props:
|
|
881
|
+
return text
|
|
882
|
+
|
|
883
|
+
resolved = text
|
|
884
|
+
for _ in range(3):
|
|
885
|
+
def _sub(m: re.Match) -> str: # noqa: E306
|
|
886
|
+
return props.get(m.group(1), m.group(0))
|
|
887
|
+
resolved_new = re.sub(r'\$\{([\w.\-]+)\}', _sub, resolved)
|
|
888
|
+
if resolved_new == resolved:
|
|
889
|
+
break
|
|
890
|
+
resolved = resolved_new
|
|
891
|
+
return resolved
|
|
892
|
+
|
|
893
|
+
|
|
871
894
|
def _scan_dep_file(text: str, rel_path: str) -> list["MigrationFinding"]:
|
|
872
895
|
"""Apply dependency rules to a build file. Returns one finding per matched rule."""
|
|
873
896
|
is_gradle = rel_path.endswith((".gradle", ".gradle.kts"))
|
|
897
|
+
if not is_gradle and rel_path.endswith(".xml"):
|
|
898
|
+
text = _resolve_maven_properties(text)
|
|
874
899
|
findings: list[MigrationFinding] = []
|
|
875
900
|
for rule in _DEP_RULES:
|
|
876
901
|
if rule.quick_filter is not None and rule.quick_filter not in text:
|
|
@@ -1198,6 +1223,7 @@ def run_migrate_check(
|
|
|
1198
1223
|
limitations.append(f"{xml_read_errors} XML file(s) could not be read and were skipped.")
|
|
1199
1224
|
|
|
1200
1225
|
dep_read_errors = 0
|
|
1226
|
+
raw_dep_findings: list[MigrationFinding] = []
|
|
1201
1227
|
for abs_path, rel_path in build_files:
|
|
1202
1228
|
try:
|
|
1203
1229
|
text = abs_path.read_text(encoding="utf-8", errors="replace")
|
|
@@ -1206,7 +1232,20 @@ def run_migrate_check(
|
|
|
1206
1232
|
continue
|
|
1207
1233
|
dep_findings = _scan_dep_file(text, rel_path)
|
|
1208
1234
|
filtered = [f for f in dep_findings if SEVERITY_ORDER.get(f.severity, 3) <= min_order]
|
|
1209
|
-
|
|
1235
|
+
raw_dep_findings.extend(filtered)
|
|
1236
|
+
|
|
1237
|
+
# Deduplicate dep findings by rule_id: same dependency in parent + child poms
|
|
1238
|
+
# is one logical finding. Keep the first occurrence (root pom sorts first).
|
|
1239
|
+
_seen_dep_rules: dict[str, int] = {} # rule_id → count
|
|
1240
|
+
for f in raw_dep_findings:
|
|
1241
|
+
_seen_dep_rules[f.rule_id] = _seen_dep_rules.get(f.rule_id, 0) + 1
|
|
1242
|
+
_dedup_dep: list[MigrationFinding] = []
|
|
1243
|
+
_emitted: set[str] = set()
|
|
1244
|
+
for f in raw_dep_findings:
|
|
1245
|
+
if f.rule_id not in _emitted:
|
|
1246
|
+
_dedup_dep.append(f)
|
|
1247
|
+
_emitted.add(f.rule_id)
|
|
1248
|
+
all_findings.extend(_dedup_dep)
|
|
1210
1249
|
|
|
1211
1250
|
if dep_read_errors:
|
|
1212
1251
|
limitations.append(f"{dep_read_errors} build file(s) could not be read and were skipped.")
|
sourcecode/repository_ir.py
CHANGED
|
@@ -105,7 +105,12 @@ class EvidenceBundle:
|
|
|
105
105
|
_PKG_RE = re.compile(r'^package\s+([\w.]+)\s*;', re.MULTILINE)
|
|
106
106
|
_IMPORT_RE = re.compile(r'^import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;', re.MULTILINE)
|
|
107
107
|
_ANN_RE = re.compile(r'^(@[\w.]+)')
|
|
108
|
-
_ANN_WITH_ARGS_RE = re.compile(
|
|
108
|
+
_ANN_WITH_ARGS_RE = re.compile(
|
|
109
|
+
r'^(@[\w.]+)\s*'
|
|
110
|
+
r'(?:\('
|
|
111
|
+
r'((?:[^()"\']*|"[^"]*"|\'[^\']*\'|\((?:[^()"\']*|"[^"]*"|\'[^\']*\')*\))*)'
|
|
112
|
+
r'\))?'
|
|
113
|
+
)
|
|
109
114
|
|
|
110
115
|
_CLASS_DECL_RE = re.compile(
|
|
111
116
|
r'(?:^|(?<=\s))'
|
|
@@ -3303,6 +3308,52 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
|
|
|
3303
3308
|
else:
|
|
3304
3309
|
security_model = "unknown"
|
|
3305
3310
|
|
|
3311
|
+
# Detect XML-based Spring Security config. When present, per-endpoint
|
|
3312
|
+
# none_detected is expected and does NOT mean the endpoint is unsecured —
|
|
3313
|
+
# security is declared in XML (HttpSecurity rules, filter chains, web.xml
|
|
3314
|
+
# security constraints). Update security_model and re-tag affected endpoints
|
|
3315
|
+
# so the output cannot be misread as "unprotected".
|
|
3316
|
+
_XML_SECURITY_RE = re.compile(
|
|
3317
|
+
r'(?:xmlns(?::[a-z]+)?="http://www\.springframework\.org/schema/security"'
|
|
3318
|
+
r'|<security:http\b'
|
|
3319
|
+
r'|<http\s[^>]*use-expressions'
|
|
3320
|
+
r'|spring-security-[2345]'
|
|
3321
|
+
r'|xmlns:security="http://www\.springframework\.org/schema/security")',
|
|
3322
|
+
re.IGNORECASE,
|
|
3323
|
+
)
|
|
3324
|
+
_xml_security_detected = False
|
|
3325
|
+
_XML_GLOBS = (
|
|
3326
|
+
"*security*.xml", "*Security*.xml",
|
|
3327
|
+
"*applicationContext*.xml", "*-context.xml", "*Context.xml",
|
|
3328
|
+
"*spring*.xml", "*Spring*.xml",
|
|
3329
|
+
)
|
|
3330
|
+
for _glob in _XML_GLOBS:
|
|
3331
|
+
for _xf in root.rglob(_glob):
|
|
3332
|
+
if "target/" in str(_xf).replace("\\", "/"):
|
|
3333
|
+
continue
|
|
3334
|
+
try:
|
|
3335
|
+
_xt = _xf.read_text(encoding="utf-8", errors="replace")
|
|
3336
|
+
except OSError:
|
|
3337
|
+
continue
|
|
3338
|
+
if _XML_SECURITY_RE.search(_xt):
|
|
3339
|
+
_xml_security_detected = True
|
|
3340
|
+
break
|
|
3341
|
+
if _xml_security_detected:
|
|
3342
|
+
break
|
|
3343
|
+
|
|
3344
|
+
if _xml_security_detected and security_model == "unknown":
|
|
3345
|
+
security_model = "xml_or_filter_chain"
|
|
3346
|
+
# Re-tag per-endpoint none_detected → xml_or_filter_chain so the output
|
|
3347
|
+
# cannot be misread as "endpoint is unprotected".
|
|
3348
|
+
for ep in endpoints:
|
|
3349
|
+
if ep.get("security", {}).get("policy") == "none_detected":
|
|
3350
|
+
ep["security"] = {"policy": "xml_or_filter_chain"}
|
|
3351
|
+
# Recompute no_security_signal (now counts only truly unknown endpoints)
|
|
3352
|
+
no_security_signal = sum(
|
|
3353
|
+
1 for e in endpoints
|
|
3354
|
+
if e.get("security", {}).get("policy") == "none_detected"
|
|
3355
|
+
)
|
|
3356
|
+
|
|
3306
3357
|
return {
|
|
3307
3358
|
"endpoints": endpoints,
|
|
3308
3359
|
"total": len(endpoints),
|
|
@@ -197,14 +197,17 @@ def _compute_event_risk(
|
|
|
197
197
|
consumer_count: int,
|
|
198
198
|
before_commit_count: int,
|
|
199
199
|
cross_module: bool,
|
|
200
|
+
sync_in_tx_count: int = 0,
|
|
200
201
|
) -> str:
|
|
201
202
|
"""Deterministic risk scoring per spec.
|
|
202
203
|
|
|
203
204
|
high: fanout > 5 OR cross-module propagation OR BEFORE_COMMIT consumers
|
|
205
|
+
OR sync @EventListener inside @Transactional publisher
|
|
204
206
|
medium: 2–5 consumers
|
|
205
207
|
low: ≤1 consumer
|
|
206
208
|
"""
|
|
207
|
-
if consumer_count > _RISK_FANOUT_HIGH or cross_module
|
|
209
|
+
if (consumer_count > _RISK_FANOUT_HIGH or cross_module
|
|
210
|
+
or before_commit_count > 0 or sync_in_tx_count > 0):
|
|
208
211
|
return "high"
|
|
209
212
|
if consumer_count >= _RISK_FANOUT_MEDIUM:
|
|
210
213
|
return "medium"
|
|
@@ -327,9 +330,23 @@ class EventTopologyOrchestrator:
|
|
|
327
330
|
# ── 7. TX context ──────────────────────────────────────────────────
|
|
328
331
|
after_commit = [c.fqn for c in consumers if c.transactional_phase == "AFTER_COMMIT"]
|
|
329
332
|
before_commit_risks = [c.fqn for c in consumers if c.transactional_phase == "BEFORE_COMMIT"]
|
|
333
|
+
|
|
334
|
+
# Detect sync @EventListener inside @Transactional publisher.
|
|
335
|
+
# Plain @EventListener fires synchronously; if the publisher method is
|
|
336
|
+
# @Transactional the listener runs inside that TX — listener exception
|
|
337
|
+
# rolls back the outer TX, and DB state may be partially committed.
|
|
338
|
+
tx_publishers = [
|
|
339
|
+
p for p in publishers
|
|
340
|
+
if "@Transactional" in ((fqn_index.get(p) or {}).get("annotations") or [])
|
|
341
|
+
]
|
|
342
|
+
sync_in_tx_risks = [
|
|
343
|
+
c.fqn for c in consumers
|
|
344
|
+
if c.type == "spring_event" and tx_publishers
|
|
345
|
+
]
|
|
330
346
|
tx_context = {
|
|
331
347
|
"after_commit_consumers": after_commit,
|
|
332
348
|
"before_commit_risks": before_commit_risks,
|
|
349
|
+
"sync_in_tx_risks": sync_in_tx_risks,
|
|
333
350
|
}
|
|
334
351
|
|
|
335
352
|
# ── 8. Cross-module detection ──────────────────────────────────────
|
|
@@ -352,6 +369,7 @@ class EventTopologyOrchestrator:
|
|
|
352
369
|
consumer_count=len(consumers),
|
|
353
370
|
before_commit_count=len(before_commit_risks),
|
|
354
371
|
cross_module=cross_module,
|
|
372
|
+
sync_in_tx_count=len(sync_in_tx_risks),
|
|
355
373
|
)
|
|
356
374
|
|
|
357
375
|
# ── 10. Confidence ─────────────────────────────────────────────────
|
|
@@ -385,6 +403,7 @@ class EventTopologyOrchestrator:
|
|
|
385
403
|
"kafka_listeners_in_repo": kafka_count,
|
|
386
404
|
"rabbit_listeners_in_repo": rabbit_count,
|
|
387
405
|
"before_commit_risk_count": len(before_commit_risks),
|
|
406
|
+
"sync_in_tx_risk_count": len(sync_in_tx_risks),
|
|
388
407
|
"level2_events": list(level2_events.keys()),
|
|
389
408
|
"cross_module": cross_module,
|
|
390
409
|
"model_build_time_ms": model.build_time_ms,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.35.
|
|
3
|
+
Version: 1.35.26
|
|
4
4
|
Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Keywords: agents,ai,codebase,context,developer-tools,llm
|
|
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
42
42
|
|
|
43
|
-

|
|
44
44
|

|
|
45
45
|
|
|
46
46
|
---
|
|
@@ -114,7 +114,7 @@ pipx install sourcecode
|
|
|
114
114
|
|
|
115
115
|
```bash
|
|
116
116
|
sourcecode version
|
|
117
|
-
# sourcecode 1.35.
|
|
117
|
+
# sourcecode 1.35.26
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
---
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=HYA42OYABbKZ43Trr1VCdpUkXMtz0AqpwPt0ENMTTjo,104
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=qh749a7ykPtGmQI1MR9y6j8TtL_jBdVYFx9YRsLqOMw,44121
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
|
|
@@ -29,7 +29,7 @@ sourcecode/graph_analyzer.py,sha256=DHR8fY69oU_Pi4SYaWboX6EoEFrctQKB9dsjpqwGMzw,
|
|
|
29
29
|
sourcecode/license.py,sha256=3JCV2OeTVttKrOGBguU5uZC0c02Stig-KLB0mP2lNiY,22742
|
|
30
30
|
sourcecode/mcp_nudge.py,sha256=5ELU_ixzh6uA83NXLOZT8h00OhL53okfQdji3jyKOjg,2917
|
|
31
31
|
sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
|
|
32
|
-
sourcecode/migrate_check.py,sha256
|
|
32
|
+
sourcecode/migrate_check.py,sha256=GuYK36DDFkwf07jbAgcoc-Ovq8ttLQNMsRqhsUilMzY,54514
|
|
33
33
|
sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
|
|
34
34
|
sourcecode/path_filters.py,sha256=ROFRQ8eSLBEMiixK9f45-RO7um4VEEcjoD5AA4I427I,3739
|
|
35
35
|
sourcecode/pr_comment_renderer.py,sha256=smHslxiG14lrytCkq5nFrFu-qTHgA-t-LFYfdrfjz2o,14423
|
|
@@ -40,14 +40,14 @@ sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,
|
|
|
40
40
|
sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
|
|
41
41
|
sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
|
|
42
42
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
43
|
-
sourcecode/repository_ir.py,sha256=
|
|
43
|
+
sourcecode/repository_ir.py,sha256=hl7Vc7o5LD0xWZ6Er7-x2IDrLurJZIfBKGPD-3cfraU,171754
|
|
44
44
|
sourcecode/ris.py,sha256=RcqLVwC-doFcKKViYDkCjZLBqf_wzLES7-F6vHEeWzE,20419
|
|
45
45
|
sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
|
|
46
46
|
sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
|
|
47
47
|
sourcecode/schema.py,sha256=aHNXDf8LGyUC8ZDE_VS9kiskC2-Oswhi_WnpdGy6HDw,24897
|
|
48
48
|
sourcecode/semantic_analyzer.py,sha256=TDuC3wzZR2DPm1mgrAg1YSLk2QzJoueS3TZAmyGGpCU,89417
|
|
49
49
|
sourcecode/serializer.py,sha256=7SBJIbpC_Lg0RGWq8jjNbF5TiuZwoP_fi0qhHnzQM8M,124386
|
|
50
|
-
sourcecode/spring_event_topology.py,sha256=
|
|
50
|
+
sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
|
|
51
51
|
sourcecode/spring_findings.py,sha256=8V91iHOg9hFgg6tLLl4FSsgrF-dBqOcO2s-K5sD_goA,5417
|
|
52
52
|
sourcecode/spring_impact.py,sha256=Ohm2k3W4Wts8Kx8Z7DIM-J-cwGtTJBWKFBsX-WkupBQ,32943
|
|
53
53
|
sourcecode/spring_model.py,sha256=IzMcM5ftw1_EHG3FGUDT7qdAMpo3eqbAE1LRuasfr_4,14739
|
|
@@ -94,8 +94,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
|
|
|
94
94
|
sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
|
|
95
95
|
sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
|
|
96
96
|
sourcecode/telemetry/transport.py,sha256=QSslxIwij8YkRWcVvxykODDrkiN_GAAEu3dUP7KIWeE,1651
|
|
97
|
-
sourcecode-1.35.
|
|
98
|
-
sourcecode-1.35.
|
|
99
|
-
sourcecode-1.35.
|
|
100
|
-
sourcecode-1.35.
|
|
101
|
-
sourcecode-1.35.
|
|
97
|
+
sourcecode-1.35.26.dist-info/METADATA,sha256=M8Y5dAsVOUGs7ij_YJC26hy4SJtJ_4fFGZZ6NpuabKQ,21297
|
|
98
|
+
sourcecode-1.35.26.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
99
|
+
sourcecode-1.35.26.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
100
|
+
sourcecode-1.35.26.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
101
|
+
sourcecode-1.35.26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|