sourcecode 1.54.0__tar.gz → 1.55.0__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 (121) hide show
  1. {sourcecode-1.54.0 → sourcecode-1.55.0}/CHANGELOG.md +26 -0
  2. {sourcecode-1.54.0 → sourcecode-1.55.0}/PKG-INFO +2 -2
  3. {sourcecode-1.54.0 → sourcecode-1.55.0}/README.md +1 -1
  4. {sourcecode-1.54.0 → sourcecode-1.55.0}/pyproject.toml +1 -1
  5. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/contract_pipeline.py +4 -2
  7. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/license.py +53 -4
  8. {sourcecode-1.54.0 → sourcecode-1.55.0}/.github/workflows/build-windows.yml +0 -0
  9. {sourcecode-1.54.0 → sourcecode-1.55.0}/.gitignore +0 -0
  10. {sourcecode-1.54.0 → sourcecode-1.55.0}/.ruff.toml +0 -0
  11. {sourcecode-1.54.0 → sourcecode-1.55.0}/CONTRIBUTING.md +0 -0
  12. {sourcecode-1.54.0 → sourcecode-1.55.0}/LICENSE +0 -0
  13. {sourcecode-1.54.0 → sourcecode-1.55.0}/SECURITY.md +0 -0
  14. {sourcecode-1.54.0 → sourcecode-1.55.0}/raw +0 -0
  15. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/adaptive_scanner.py +0 -0
  16. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/architecture_analyzer.py +0 -0
  17. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/architecture_summary.py +0 -0
  18. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/ast_extractor.py +0 -0
  19. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/cache.py +0 -0
  20. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/canonical_ir.py +0 -0
  21. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/cir_graphs.py +0 -0
  22. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/classifier.py +0 -0
  23. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/cli.py +0 -0
  24. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  25. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/confidence_analyzer.py +0 -0
  26. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/context_scorer.py +0 -0
  27. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/context_summarizer.py +0 -0
  28. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/contract_model.py +0 -0
  29. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/coverage_parser.py +0 -0
  30. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/dependency_analyzer.py +0 -0
  31. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/__init__.py +0 -0
  32. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/base.py +0 -0
  33. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  34. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/dart.py +0 -0
  35. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/dotnet.py +0 -0
  36. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/elixir.py +0 -0
  37. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/go.py +0 -0
  38. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/heuristic.py +0 -0
  39. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/hybrid.py +0 -0
  40. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/java.py +0 -0
  41. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  42. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/nodejs.py +0 -0
  43. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/parsers.py +0 -0
  44. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/php.py +0 -0
  45. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/project.py +0 -0
  46. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/python.py +0 -0
  47. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/ruby.py +0 -0
  48. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/rust.py +0 -0
  49. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/systems.py +0 -0
  50. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/terraform.py +0 -0
  51. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/detectors/tooling.py +0 -0
  52. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/doc_analyzer.py +0 -0
  53. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  54. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/env_analyzer.py +0 -0
  55. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/error_schema.py +0 -0
  56. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/explain.py +0 -0
  57. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/file_chunker.py +0 -0
  58. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/file_classifier.py +0 -0
  59. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/flow_analyzer.py +0 -0
  60. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/format_contract.py +0 -0
  61. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/fqn_utils.py +0 -0
  62. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/git_analyzer.py +0 -0
  63. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/graph_analyzer.py +0 -0
  64. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/integration_detector.py +0 -0
  65. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/__init__.py +0 -0
  66. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  67. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  68. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  69. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  70. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  71. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/orchestrator.py +0 -0
  72. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/registry.py +0 -0
  73. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/runner.py +0 -0
  74. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp/server.py +0 -0
  75. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/mcp_nudge.py +0 -0
  76. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/metrics_analyzer.py +0 -0
  77. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/migrate_check.py +0 -0
  78. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/openapi_surface.py +0 -0
  79. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/output_budget.py +0 -0
  80. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/path_filters.py +0 -0
  81. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/pr_comment_renderer.py +0 -0
  82. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/pr_impact.py +0 -0
  83. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/prepare_context.py +0 -0
  84. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/progress.py +0 -0
  85. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/ranking_engine.py +0 -0
  86. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/redactor.py +0 -0
  87. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/relevance_scorer.py +0 -0
  88. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/rename_refactor.py +0 -0
  89. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/repo_classifier.py +0 -0
  90. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/repository_ir.py +0 -0
  91. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/ris.py +0 -0
  92. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/runtime_classifier.py +0 -0
  93. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/scanner.py +0 -0
  94. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/schema.py +0 -0
  95. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/security_config.py +0 -0
  96. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/semantic_analyzer.py +0 -0
  97. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/serializer.py +0 -0
  98. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/spring_event_topology.py +0 -0
  99. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/spring_findings.py +0 -0
  100. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/spring_impact.py +0 -0
  101. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/spring_model.py +0 -0
  102. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/spring_security_audit.py +0 -0
  103. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/spring_semantic.py +0 -0
  104. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
  105. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/summarizer.py +0 -0
  106. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/telemetry/__init__.py +0 -0
  107. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/telemetry/config.py +0 -0
  108. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/telemetry/consent.py +0 -0
  109. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/telemetry/events.py +0 -0
  110. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/telemetry/filters.py +0 -0
  111. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/telemetry/transport.py +0 -0
  112. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/tree_utils.py +0 -0
  113. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/validation_surface.py +0 -0
  114. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/version_check.py +0 -0
  115. {sourcecode-1.54.0 → sourcecode-1.55.0}/src/sourcecode/workspace.py +0 -0
  116. {sourcecode-1.54.0 → sourcecode-1.55.0}/supabase/functions/README.md +0 -0
  117. {sourcecode-1.54.0 → sourcecode-1.55.0}/supabase/functions/get-license/index.ts +0 -0
  118. {sourcecode-1.54.0 → sourcecode-1.55.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  119. {sourcecode-1.54.0 → sourcecode-1.55.0}/supabase/functions/telemetry/index.ts +0 -0
  120. {sourcecode-1.54.0 → sourcecode-1.55.0}/supabase/sql/license_event_ordering.sql +0 -0
  121. {sourcecode-1.54.0 → sourcecode-1.55.0}/supabase/sql/telemetry_events.sql +0 -0
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.55.0] — 2026-06-19
4
+
5
+ ### Security
6
+ - **License secret no longer written world-readable.** `~/.sourcecode/license.json`
7
+ stores the Pro `license_key` and account email but was written with the default
8
+ umask (typically `0644`, dir `0755`), so any other local user could read the
9
+ credential on a shared host. Now the directory is created/tightened to `0700` and
10
+ the file is `chmod 0600` on the tmp file *before* the atomic rename (no
11
+ world-readable window at the final path). `_secure_dir()` is applied at every
12
+ write site (activation, license file, delta-run counter).
13
+
14
+ - **License-endpoint override now scheme-validated.** `SOURCECODE_SUPABASE_URL` was
15
+ trusted verbatim; an `http://` value sent the license key over plaintext. The
16
+ override is now accepted only when `https://` (any host) or `http://` to loopback
17
+ (preserves Supabase local dev on `http://127.0.0.1:54321`); anything else is
18
+ rejected back to the default endpoint with a warning.
19
+
20
+ - **Hardened the symbol grep in the contract pipeline.** The deep-scan symbol
21
+ search ran `grep <symbol> .` with the symbol in pattern position and as a regex,
22
+ while the Python fallback matched literally — inconsistent, and a leading-dash
23
+ symbol could be parsed as a flag. Switched to `grep -F -e <symbol> -- .`: literal
24
+ match (matches the fallback, removes the regex/ReDoS surface) and `-e`/`--` guard
25
+ option parsing. No `shell=True` anywhere; this is defense-in-depth on argv.
26
+
27
+ Dependency audit (`pip-audit`): no known vulnerabilities in runtime dependencies.
28
+
3
29
  ## [1.54.0] — 2026-06-19
4
30
 
5
31
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.54.0
3
+ Version: 1.55.0
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
- ![Version](https://img.shields.io/badge/version-1.54.0-blue)
43
+ ![Version](https://img.shields.io/badge/version-1.55.0-blue)
44
44
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
45
45
 
46
46
  ---
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
4
4
 
5
- ![Version](https://img.shields.io/badge/version-1.54.0-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.55.0-blue)
6
6
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
7
7
 
8
8
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.54.0"
7
+ version = "1.55.0"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.54.0"
3
+ __version__ = "1.55.0"
@@ -634,11 +634,13 @@ def _find_symbol_files(
634
634
  try:
635
635
  result = subprocess.run(
636
636
  [
637
- "grep", "-rl",
637
+ "grep", "-rlF",
638
638
  "--include=*.ts", "--include=*.tsx",
639
639
  "--include=*.js", "--include=*.jsx",
640
640
  "--include=*.py", "--include=*.java",
641
- symbol, ".",
641
+ # -e guards a symbol that begins with '-' from being parsed as a
642
+ # flag; -- terminates options so the search root can't be either.
643
+ "-e", symbol, "--", ".",
642
644
  ],
643
645
  cwd=str(root),
644
646
  capture_output=True,
@@ -45,7 +45,33 @@ _DEFAULT_SUPABASE_URL: str = "https://qkndlmyekvujjdgthtmz.supabase.co"
45
45
  # Paste your project's anon key here so `sourcecode activate` works out of the
46
46
  # box; env var still overrides for testing against another project.
47
47
  _DEFAULT_SUPABASE_ANON_KEY: str = "sb_publishable_qiJFLWjbBbTqjg-fb0mAGA_cl8PBOKH"
48
- _SUPABASE_URL: str = os.environ.get("SOURCECODE_SUPABASE_URL", _DEFAULT_SUPABASE_URL)
48
+ def _safe_supabase_url(override: "Optional[str]") -> str:
49
+ """Validate the SOURCECODE_SUPABASE_URL override before trusting it.
50
+
51
+ The license key is POSTed to this host, so a plaintext ``http://`` endpoint
52
+ would expose the credential on the wire. Allow ``https://`` to any host, and
53
+ ``http://`` only to loopback (Supabase local dev serves on
54
+ ``http://127.0.0.1:54321``). Anything else is rejected back to the default.
55
+ """
56
+ if not override or override == _DEFAULT_SUPABASE_URL:
57
+ return _DEFAULT_SUPABASE_URL
58
+ from urllib.parse import urlparse
59
+
60
+ parsed = urlparse(override)
61
+ host = (parsed.hostname or "").lower()
62
+ is_loopback = host in {"localhost", "127.0.0.1", "::1"}
63
+ if parsed.scheme == "https" or (parsed.scheme == "http" and is_loopback):
64
+ return override
65
+ sys.stderr.write(
66
+ f"[sourcecode] WARNING: ignoring SOURCECODE_SUPABASE_URL={override!r} — "
67
+ "must be https:// (or http:// to localhost). Using the default endpoint "
68
+ "to avoid sending the license key over plaintext.\n"
69
+ )
70
+ sys.stderr.flush()
71
+ return _DEFAULT_SUPABASE_URL
72
+
73
+
74
+ _SUPABASE_URL: str = _safe_supabase_url(os.environ.get("SOURCECODE_SUPABASE_URL"))
49
75
  _SUPABASE_ANON_KEY: str = os.environ.get(
50
76
  "SOURCECODE_SUPABASE_ANON_KEY",
51
77
  _DEFAULT_SUPABASE_ANON_KEY,
@@ -152,12 +178,35 @@ _license_data: Optional[dict] = None
152
178
  is_pro: bool = False
153
179
 
154
180
 
181
+ def _secure_dir() -> None:
182
+ """Create ~/.sourcecode owner-only (0700). Holds the license secret.
183
+
184
+ ``mkdir(mode=...)`` is ignored when the dir already exists, so chmod
185
+ unconditionally. Best-effort: a chmod failure (e.g. Windows) is non-fatal.
186
+ """
187
+ try:
188
+ _LICENSE_DIR.mkdir(parents=True, exist_ok=True)
189
+ os.chmod(_LICENSE_DIR, 0o700)
190
+ except OSError:
191
+ pass
192
+
193
+
155
194
  def _write_license_file(data: dict) -> None:
156
- """Atomically write license data via tmp file + rename."""
195
+ """Atomically write license data via tmp file + rename, owner-only (0600).
196
+
197
+ The payload contains the Pro ``license_key`` and account email, so the file
198
+ must not be world/group-readable on shared hosts. Permissions are set on the
199
+ tmp file *before* the rename so there is no readable window at the final path.
200
+ """
201
+ _secure_dir()
157
202
  payload = json.dumps(data, indent=2, ensure_ascii=False).encode("utf-8")
158
203
  tmp = _LICENSE_FILE.with_suffix(".tmp")
159
204
  try:
160
205
  tmp.write_bytes(payload)
206
+ try:
207
+ os.chmod(tmp, 0o600)
208
+ except OSError:
209
+ pass
161
210
  tmp.replace(_LICENSE_FILE)
162
211
  except Exception:
163
212
  try:
@@ -192,7 +241,7 @@ def check_delta_free_tier(repo_path: str) -> "tuple[bool, int, int]":
192
241
  new_used = used + 1
193
242
  runs[key] = new_used
194
243
  try:
195
- _LICENSE_DIR.mkdir(parents=True, exist_ok=True)
244
+ _secure_dir()
196
245
  tmp = _DELTA_RUNS_FILE.with_suffix(".tmp")
197
246
  tmp.write_text(json.dumps(runs, indent=2, ensure_ascii=False), encoding="utf-8")
198
247
  tmp.replace(_DELTA_RUNS_FILE)
@@ -506,7 +555,7 @@ def activate_license(license_key: str) -> None:
506
555
  _emit_telemetry("activation", feature="key", success=False, error_kind="NotPro")
507
556
  _fail("not_pro", "This license is not a Pro license.")
508
557
 
509
- _LICENSE_DIR.mkdir(parents=True, exist_ok=True)
558
+ _secure_dir()
510
559
  now = datetime.now(timezone.utc).isoformat()
511
560
  data = {
512
561
  "license_key": license_key,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes