sourcecode 1.31.26__py3-none-any.whl → 1.31.27__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.31.26"
3
+ __version__ = "1.31.27"
@@ -332,6 +332,7 @@ def ir_dict_to_canonical(
332
332
  "spring_events": ir.get("spring_events") or {},
333
333
  "score_basis": (ir.get("impact") or {}).get("score_basis", "none"),
334
334
  "reverse_graph_size": len(ir.get("reverse_graph") or {}),
335
+ "security_model": ir.get("security_model", "unknown"),
335
336
  }
336
337
 
337
338
  cir_hash = _compute_cir_hash(
@@ -452,6 +453,7 @@ def project_endpoint_surface(cir: CanonicalRepositoryIR) -> dict:
452
453
  "endpoints": endpoints,
453
454
  "total": len(endpoints),
454
455
  "no_security_signal": no_security_signal,
456
+ "security_model": cir.metadata.get("security_model", "unknown"),
455
457
  # Legacy field alias — same count, kept for backward compat
456
458
  "undocumented": no_security_signal,
457
459
  }
sourcecode/cli.py CHANGED
@@ -1127,10 +1127,14 @@ def main(
1127
1127
 
1128
1128
  # --compact implicitly enables lightweight analysis passes so that
1129
1129
  # dependency_summary, env_summary and code_notes_summary are never null.
1130
+ # architecture=True is also enabled so that architecture.confidence is
1131
+ # consistent with --agent (which auto-enables architecture). The
1132
+ # ArchitectureAnalyzer is path-based and adds negligible latency.
1130
1133
  if compact:
1131
1134
  dependencies = True
1132
1135
  env_map = True
1133
1136
  code_notes = True
1137
+ architecture = True
1134
1138
 
1135
1139
  dependency_analyzer = DependencyAnalyzer() if dependencies else None
1136
1140
  graph_analyzer = GraphAnalyzer() if graph_modules else None
sourcecode/mcp/server.py CHANGED
@@ -158,9 +158,12 @@ def get_endpoints(repo_path: str = ".") -> dict:
158
158
  Returns: endpoints list with method, path, controller, handler fields;
159
159
  security dict when authorization annotations are present
160
160
  (policy: roles_allowed|permit_all|deny_all|authenticated|...);
161
- total (int) and no_security_signal (int) counts.
162
- no_security_signal counts endpoints with no recognized auth annotation
163
- repos using framework-level auth (e.g. Keycloak) may show high counts.
161
+ total (int), no_security_signal (int), and security_model (str) fields.
162
+ no_security_signal counts endpoints with no recognized auth annotation.
163
+ security_model values: "filter_based" (centralized Spring Security config
164
+ high no_security_signal is expected and does NOT mean endpoints are unprotected),
165
+ "annotation_based" (per-endpoint annotations only), "mixed" (both),
166
+ "unknown" (no security signals detected).
164
167
  Supports Spring MVC (@GetMapping etc.) and JAX-RS (@GET/@POST etc.).
165
168
  Security annotations detected: @RolesAllowed, @PermitAll, @DenyAll,
166
169
  @Authenticated, @PreAuthorize, @Secured, @SecurityRequirement, @M3FiltroSeguridad.
@@ -192,6 +192,15 @@ _SECURITY_MARKER_ANNOTATIONS: frozenset[str] = frozenset({
192
192
  "@PermitAll", "@DenyAll", "@Authenticated",
193
193
  })
194
194
 
195
+ # Annotations on config classes that indicate a centralized security filter chain
196
+ # (Spring Security / Spring Boot). When present, per-endpoint no_security_signal
197
+ # is expected and does NOT mean endpoints are unprotected.
198
+ _FILTER_SECURITY_ANNOTATIONS: frozenset[str] = frozenset({
199
+ "@EnableWebSecurity",
200
+ "@EnableMethodSecurity",
201
+ "@EnableGlobalMethodSecurity",
202
+ })
203
+
195
204
  _MODIFIER_WORDS: frozenset[str] = frozenset({
196
205
  "public", "private", "protected", "static", "final", "abstract",
197
206
  "synchronized", "native", "strictfp", "transient", "volatile", "default",
@@ -1711,6 +1720,7 @@ def _detect_subsystems(all_fqns: list[str], relations: list[RelationEdge]) -> li
1711
1720
  "label": label,
1712
1721
  "package_prefix": pkg_prefix,
1713
1722
  "member_count": len(members),
1723
+ "members": members,
1714
1724
  "summary": summary,
1715
1725
  })
1716
1726
  return result
@@ -2132,17 +2142,47 @@ def _assemble(
2132
2142
  "change_set": change_set_out,
2133
2143
  }
2134
2144
 
2135
- _route_surface = _build_route_surface(sorted_syms, route_diffs, extends_map={
2145
+ _extends_map = {
2136
2146
  e.from_symbol: e.to_symbol.split(".")[-1]
2137
2147
  for e in sorted_rels if e.type == "extends"
2138
- })
2148
+ }
2149
+ _route_surface = _build_route_surface(sorted_syms, route_diffs, extends_map=_extends_map)
2139
2150
  _analysis_gaps = _compute_analysis_gaps(sorted_syms, spring_summary, _route_surface, sorted_rels)
2140
2151
 
2152
+ # Detect filter-based security model for the assembled IR.
2153
+ # Stored here so CIR projections (project_endpoint_surface) can read it without
2154
+ # re-parsing symbols.
2155
+ _class_syms_asm = [s for s in sorted_syms if s.type in ("class", "interface")]
2156
+ _filter_based_asm = (
2157
+ any(
2158
+ ann in _FILTER_SECURITY_ANNOTATIONS
2159
+ for sym in _class_syms_asm
2160
+ for ann in sym.annotations
2161
+ )
2162
+ or any(
2163
+ _extends_map.get(sym.symbol, "") == "WebSecurityConfigurerAdapter"
2164
+ for sym in _class_syms_asm
2165
+ )
2166
+ )
2167
+ _has_ann_sec_asm = any(
2168
+ r.get("security_annotations") for r in _route_surface
2169
+ if isinstance(r, dict)
2170
+ )
2171
+ if _filter_based_asm and _has_ann_sec_asm:
2172
+ _security_model_asm = "mixed"
2173
+ elif _filter_based_asm:
2174
+ _security_model_asm = "filter_based"
2175
+ elif _has_ann_sec_asm:
2176
+ _security_model_asm = "annotation_based"
2177
+ else:
2178
+ _security_model_asm = "unknown"
2179
+
2141
2180
  return {
2142
2181
  **_base,
2143
2182
  "route_surface": _route_surface,
2144
2183
  "spring_events": _spring_events,
2145
2184
  "analysis_gaps": _analysis_gaps,
2185
+ "security_model": _security_model_asm,
2146
2186
  "audit": {
2147
2187
  "dropped_fields": dropped_fields,
2148
2188
  },
@@ -2908,10 +2948,39 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
2908
2948
  # Note: repos may use framework-level security (e.g. Keycloak itself) with no
2909
2949
  # per-endpoint annotations — this count reflects annotation-based coverage only.
2910
2950
  no_security_signal = sum(1 for e in endpoints if "security" not in e)
2951
+
2952
+ # Detect filter-based security: centralized Spring Security config class.
2953
+ # When present, high no_security_signal is expected — security is enforced by
2954
+ # the filter chain, not per-endpoint annotations.
2955
+ _class_syms = [s for s in all_symbols if s.type in ("class", "interface")]
2956
+ _filter_based = (
2957
+ # Config class annotated with EnableWebSecurity / EnableMethodSecurity
2958
+ any(
2959
+ ann in _FILTER_SECURITY_ANNOTATIONS
2960
+ for sym in _class_syms
2961
+ for ann in sym.annotations
2962
+ )
2963
+ # Class extends WebSecurityConfigurerAdapter (pre-Spring 5.7 style)
2964
+ or any(
2965
+ extends_map.get(sym.symbol, "") == "WebSecurityConfigurerAdapter"
2966
+ for sym in _class_syms
2967
+ )
2968
+ )
2969
+ _has_annotation_security = any("security" in e for e in endpoints)
2970
+ if _filter_based and _has_annotation_security:
2971
+ security_model = "mixed"
2972
+ elif _filter_based:
2973
+ security_model = "filter_based"
2974
+ elif _has_annotation_security:
2975
+ security_model = "annotation_based"
2976
+ else:
2977
+ security_model = "unknown"
2978
+
2911
2979
  return {
2912
2980
  "endpoints": endpoints,
2913
2981
  "total": len(endpoints),
2914
2982
  "no_security_signal": no_security_signal,
2983
+ "security_model": security_model,
2915
2984
  # Keep legacy field name for backward compat, now means same as no_security_signal
2916
2985
  "undocumented": no_security_signal,
2917
2986
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.31.26
3
+ Version: 1.31.27
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -225,7 +225,7 @@ Description-Content-Type: text/markdown
225
225
 
226
226
  **AI-ready change intelligence for Java/Spring enterprise monoliths.**
227
227
 
228
- ![Version](https://img.shields.io/badge/version-1.31.26-blue)
228
+ ![Version](https://img.shields.io/badge/version-1.31.27-blue)
229
229
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
230
230
 
231
231
  ---
@@ -263,7 +263,7 @@ pipx install sourcecode
263
263
 
264
264
  ```bash
265
265
  sourcecode version
266
- # sourcecode 1.31.26
266
+ # sourcecode 1.31.27
267
267
  ```
268
268
 
269
269
  ---
@@ -1,12 +1,12 @@
1
- sourcecode/__init__.py,sha256=_0ipTmYmgGPKfHgZjMg0xBa-rDMdz89QQ6mQQ0rq9ng,104
1
+ sourcecode/__init__.py,sha256=VXhPMdJM6x_WGp7qMeGlW63Lz-PmJxDFOdXqGhLNfm4,104
2
2
  sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
3
3
  sourcecode/architecture_analyzer.py,sha256=Ry3aYT9dc7XuLmWLT5IZ93RkCf_P14Qtew0nGPvUl_8,42184
4
4
  sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
5
5
  sourcecode/ast_extractor.py,sha256=_btmeOJIe3t-NicF94D5ZAesa2YIJ0_QNExGnbHxGFE,50578
6
6
  sourcecode/cache.py,sha256=TiYa3ECjBKtvlfCk7GvQ9v6gZkAITpH3ow9PubA7sUo,22946
7
- sourcecode/canonical_ir.py,sha256=NZu0XICv__hkQGKzW2LNQLRqb1L28K2p_WQCQKS5Zlk,23141
7
+ sourcecode/canonical_ir.py,sha256=_HM3AUmKSdna9u4dCoU6rpgSA6HdF8gzOKZykIUCNGY,23277
8
8
  sourcecode/classifier.py,sha256=yWeq6agTjkFa3zuNa-gdVIHtjoBoPoVlJnX-b7tdVJs,7851
9
- sourcecode/cli.py,sha256=illoqX3tEq9E_i2QEP4aUWPwFxb1IYdWwUbOobv4pyw,151737
9
+ sourcecode/cli.py,sha256=8qcr6G5RF17EqtLsM7Weu3G6Pb3GbfgudK-Knx01-0w,151980
10
10
  sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
11
11
  sourcecode/confidence_analyzer.py,sha256=_jckZSxksV-OU38vbkxfVNBnWCtlCq8Vwfg23x1uspA,19054
12
12
  sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
@@ -32,7 +32,7 @@ sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,
32
32
  sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
33
33
  sourcecode/relevance_scorer.py,sha256=MYF4FFkveAQps9SmTeTlh6ODiBz2F--_hWNeHMLtUHQ,8405
34
34
  sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
35
- sourcecode/repository_ir.py,sha256=sp6IdcZbFAQjznUthMBu_6Mu5RBxVP72d5Vw0hKnH7o,148437
35
+ sourcecode/repository_ir.py,sha256=gzgveIWxgT77JwUxS2dxEuLp6UbVnrHEkVo4a8x4QfY,151066
36
36
  sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
37
37
  sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
38
38
  sourcecode/schema.py,sha256=aHNXDf8LGyUC8ZDE_VS9kiskC2-Oswhi_WnpdGy6HDw,24897
@@ -64,7 +64,7 @@ sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG6
64
64
  sourcecode/detectors/tooling.py,sha256=8CKbtxwQoABP-WyBRNmdAmHDOvAH57AR1cF4UKuWEdQ,2074
65
65
  sourcecode/mcp/__init__.py,sha256=XU4HfRGbdid8wdUA0x_4f7uKZD1z3mv_XUY_WU_T9Mw,179
66
66
  sourcecode/mcp/runner.py,sha256=7PnFjKYbgxFeDnqVeSntXHxZX7ZtK3-krDkEuVjI24M,1386
67
- sourcecode/mcp/server.py,sha256=8dktvqpjk2dYdnTsE0KHjZFkAR-3AQNcC3n-ITMmwKk,19864
67
+ sourcecode/mcp/server.py,sha256=BqoXi2Ncu7huGVie9AbsUNAxq9cbASLJ_Zgf68aZWsI,20122
68
68
  sourcecode/mcp/onboarding/__init__.py,sha256=sj2PWqEBmMc4zBNkomg89WtL0M6S7A9yb7_wAuSWNP4,66
69
69
  sourcecode/mcp/onboarding/applier.py,sha256=yfSMT0NKdZsjavtLkC8yQ7OtkfepOl5IXGByqg6bdEY,1894
70
70
  sourcecode/mcp/onboarding/backup.py,sha256=ihqGOR8QTX8HASRSEDyfFyXr5bkXrygPHamv4p9KTmk,1452
@@ -76,8 +76,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
76
76
  sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
77
77
  sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
78
78
  sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
79
- sourcecode-1.31.26.dist-info/METADATA,sha256=SyYqOW4T_PHgFlrJp2dwlPbiET16QlhdlduJB3LAtzU,31103
80
- sourcecode-1.31.26.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
81
- sourcecode-1.31.26.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
82
- sourcecode-1.31.26.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
83
- sourcecode-1.31.26.dist-info/RECORD,,
79
+ sourcecode-1.31.27.dist-info/METADATA,sha256=yC3wqkw7X5sccpT8RShxa7uR2_fQsik7FCAJJZ33yY0,31103
80
+ sourcecode-1.31.27.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
81
+ sourcecode-1.31.27.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
82
+ sourcecode-1.31.27.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
83
+ sourcecode-1.31.27.dist-info/RECORD,,