sourcecode 4.0.1__tar.gz → 4.2.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.
- {sourcecode-4.0.1 → sourcecode-4.2.0}/CHANGELOG.md +217 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/PKG-INFO +29 -3
- {sourcecode-4.0.1 → sourcecode-4.2.0}/README.md +28 -2
- {sourcecode-4.0.1 → sourcecode-4.2.0}/pyproject.toml +1 -1
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/caller_reach.py +16 -7
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/canonical_ir.py +29 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/cli.py +247 -3
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/contract_diff.py +5 -4
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/envelope.py +10 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/environment_resolution.py +100 -6
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/facts/registry.json +15 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/git_checkout.py +5 -2
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/non_coverage.py +33 -5
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/posture.py +483 -7
- sourcecode-4.2.0/src/sourcecode/release_info.py +123 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/remedies.py +18 -1
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/risk.py +73 -15
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/rule_catalog.py +1 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/schemas/envelope-v1.schema.json +8 -1
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/security_config_scan.py +132 -1
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_impact.py +14 -3
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_security_audit.py +16 -2
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/verify_repo.py +5 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/.gitignore +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/.ruff.toml +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/CONTRIBUTING.md +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/LICENSE +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/SECURITY.md +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/raw +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/scripts/compare_integration_engines.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/scripts/customer_smoke_test.sh +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/scripts/generate_jdk_exports.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/scripts/perf_harness.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/archetype.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/architectural_baseline.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/architectural_delta.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/cache.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/cache_model.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/call_surface.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/caller_metrics.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/chain_rules.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/change_plan.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/compare.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/constraint_diff.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/context_cache.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/context_graph.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/contract_init.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/defect_identity.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/degradation.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/deployment_prefix.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/dynamic_argument_surface.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/endpoint_literals.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/endpoint_metrics.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/evidence_provider.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/explain.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/facts/__init__.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/filter_surface.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/graph_evidence.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/hibernate_strat.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/identity_fallback.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/integration_coordinates.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/jdk_exports.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/license.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/registry.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp/server.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/migration_blast.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/parse_cache.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/path_admission.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/perf.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/pipe_contract.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/readiness_timeline.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/reconciliation.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/reference_facts.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/repository_ir.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/__init__.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/context.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/errors.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/executor.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/planner.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/query.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/request.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/resolution.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/result.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/retriever.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/runtime.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_graph.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_impact.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_intf.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_struct.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_txsec.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/ris.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/security_config.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/security_posture.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/semantic_impact_engine.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/semantic_integration_engine.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/semantic_services.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_profiles.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_properties.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/target_admission.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/test_gap_ranking.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/test_sources.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/token_estimate.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/type_usage_surface.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/validation_inference.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/verify_edit.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/verify_rules.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/version_check.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/supabase/functions/README.md +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/supabase/sql/licensing_schema.sql +0 -0
- {sourcecode-4.0.1 → sourcecode-4.2.0}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -2,6 +2,223 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [4.2.0] — 2026-08-03
|
|
6
|
+
|
|
7
|
+
Minor, not a patch, by this project's own criterion: two published figures move
|
|
8
|
+
and a verdict changes, so a pipeline reading either can change colour.
|
|
9
|
+
`posture --resolve-environments` answers `NOT DECIDED IN THIS REPOSITORY` where
|
|
10
|
+
it answered `artefacts_disagree` on a repository whose build properties nothing
|
|
11
|
+
consumes (C1-22), and `ask risk` publishes defects where it published findings,
|
|
12
|
+
so `total_defects` and `by_band` move (C1-23) — 20 → 15 on BroadleafCommerce.
|
|
13
|
+
`risk` also drops `rule_id` for `rule_ids`: it is in the `experimental` tier,
|
|
14
|
+
whose published promise is exactly that its shape may change in a minor.
|
|
15
|
+
|
|
16
|
+
Field evaluation #9 (8/10, same 3 337-file monolith as #6–#8) opened nine rows
|
|
17
|
+
and this release closes seven of them. The two left open are open on purpose:
|
|
18
|
+
`C3-39` needs a Windows runner before it needs a fix, and `P-7` is a pricing
|
|
19
|
+
decision. Every complaint was re-measured against HEAD before a row was written
|
|
20
|
+
— five reproduced, one was half false, one was not reproducible here, and one
|
|
21
|
+
was a capability that already shipped.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`ask posture --diff-ref <base>:<head>` and `--since <ref>` — one profile set,
|
|
26
|
+
two git refs** (roadmap M10.5 item 1). `--diff` holds the tree fixed and moves
|
|
27
|
+
the profile set; this holds the profile set fixed and moves the tree, which is
|
|
28
|
+
the question a pull request asks: *does this change what a client can reach
|
|
29
|
+
without authenticating?* `--since` compares a ref with the working tree,
|
|
30
|
+
uncommitted edits included. Both refs are read with `git archive`: nothing is
|
|
31
|
+
written to the repository, no worktree is created and the index is not touched.
|
|
32
|
+
`docs/posture.md` had declared this as the one requested extension with no
|
|
33
|
+
feature behind it.
|
|
34
|
+
- **`--fail-on opened|any|never` — the gate half.** Default `never`, so no
|
|
35
|
+
invocation that worked yesterday changes colour today; `opened` exits 1 when a
|
|
36
|
+
request a readable rule leaves reachable unauthenticated was not one before.
|
|
37
|
+
The payload is emitted before the exit code, because a red pipeline with no
|
|
38
|
+
statement of what opened is a gate nobody keeps. It requires a ref comparison —
|
|
39
|
+
gating a single run would gate on pre-existing debt, which is `spring-audit --ci`.
|
|
40
|
+
- **`endpoint_request_identity` registered as a fact** (ADR-0008 R11,
|
|
41
|
+
`sourcecode.canonical_ir:request_identity`). The comparison unit is the request
|
|
42
|
+
(`METHOD path`), not the endpoint id, which carries the controller FQN and the
|
|
43
|
+
handler symbol and therefore changes when a handler moves between classes
|
|
44
|
+
without the URL changing. `contract_diff` re-derived the same identity and now
|
|
45
|
+
binds to the authority. Path-variable patterns are deliberately **not**
|
|
46
|
+
normalised: `{id:.*}` and `{id:[^0-9].*}` are different URLs to a caller, and
|
|
47
|
+
one field repository moved 93 requests with exactly that edit in a single commit.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- **`ask endpoints --compact` — the census instead of the rows** (C2-22, field
|
|
52
|
+
evaluation #9). Asked how exposed a repository was, the command answered with
|
|
53
|
+
1.9 MB of JSON for 3 574 endpoints and no summary mode, and the evaluator
|
|
54
|
+
aggregated it by hand. Every answer now carries an `exposure` block — counts by
|
|
55
|
+
method, by security policy, by controller and by first path area — and
|
|
56
|
+
`--compact` returns it without the rows, declaring what it dropped. Two rules
|
|
57
|
+
it obeys against itself: the census is measured over the population the filters
|
|
58
|
+
chose and never over what `--limit` left rendered, and every ranked rollup
|
|
59
|
+
states its own total when it cuts. Measured on BroadleafCommerce: 95 KB → 11 KB,
|
|
60
|
+
every count unchanged. (The other half of the complaint is recorded as false:
|
|
61
|
+
`spring-audit` has shipped `--compact`, `--min-severity` and `--scope` for
|
|
62
|
+
releases.)
|
|
63
|
+
|
|
64
|
+
- **NC-007 — the endpoint population is declared** (CL-12, field evaluation #9).
|
|
65
|
+
`endpoints` projects Spring handler mappings; a servlet declared in `web.xml`
|
|
66
|
+
or registered as a `ServletRegistrationBean` is reachable over HTTP and is in
|
|
67
|
+
no part of that model. Six published non-coverage rows are exactly what let a
|
|
68
|
+
reader treat the list as the whole HTTP surface, and the evaluation lost a
|
|
69
|
+
monitoring console that way — at a path behind the credential the same run
|
|
70
|
+
reported as SEC-007. The row is published in the `endpoints` payload and inside
|
|
71
|
+
`posture.endpoints`, because the reader who counts open requests is reading
|
|
72
|
+
`posture`. Admission of a servlet surface is deliberately **not** in this
|
|
73
|
+
change: it is a second population and merging it into `endpoints.total` would
|
|
74
|
+
move a published count and every ratio keyed on it.
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **A reach figure that did not name its scope** (C2-23, field evaluation #9).
|
|
79
|
+
`risk` reported `endpoints_reached: 1` for a symbol whose class `impact-chain`
|
|
80
|
+
said reached 8 — both correct, both documented, in two payloads, so reconciling
|
|
81
|
+
them cost the reader three unit notes. Each row now carries
|
|
82
|
+
`endpoints_reached_basis`: what the figure is a reach *of* (a defect sits at the
|
|
83
|
+
symbol that carries the remedy, so a method-level defect walks from the method)
|
|
84
|
+
and, where that is narrower than a class, the command that answers the wider
|
|
85
|
+
question. The relationship is asserted by measurement rather than stated in
|
|
86
|
+
prose.
|
|
87
|
+
|
|
88
|
+
- **The remedy registry now follows the status, not only the limitation** (C4-13,
|
|
89
|
+
field evaluation #9 — the sixth appearance of the C4 shape). The evaluation met
|
|
90
|
+
`verify`'s `contract_count: 0` → `unverified` → exit 2 under a read-only
|
|
91
|
+
constraint, concluded the command was unusable and asked for a `--dry-run` that
|
|
92
|
+
shipped in 3.7.0. The state named one next move and the reader could not take
|
|
93
|
+
it. `Remedy` gained `also` (the further options an answer needs — a remedy is an
|
|
94
|
+
invocation, not a flag), the new entry is `ask verify . --init --dry-run`, and
|
|
95
|
+
the `unverified` reason names both forms. The guard grew with it: every option
|
|
96
|
+
of an invocation must be accepted by the command it names, and the README check
|
|
97
|
+
matches a line carrying the command with all of its options, so a front page
|
|
98
|
+
that lists `--init` and omits `--dry-run` fails the build.
|
|
99
|
+
|
|
100
|
+
- **The hub-class guard contradicted the payload beside it** (C2-21, field
|
|
101
|
+
evaluation #9). It printed *"symbol has > 500 direct callers"* while the same
|
|
102
|
+
run published 215 of them on openmrs-core's `Context`: the guard trips on
|
|
103
|
+
caller **reference sites** (method-level keys included) and the payload
|
|
104
|
+
publishes caller **classes**, so the stated reason was checkably false while
|
|
105
|
+
the decision was right. `caller_reach.bfs_callers` now returns the population it
|
|
106
|
+
read, and the warning names it with its unit next to the class count it takes
|
|
107
|
+
from `caller_metrics.caller_classes` — *"792 caller reference sites … exceed the
|
|
108
|
+
cap of 500 … The 215 direct caller classes this run publishes stay exact"*.
|
|
109
|
+
|
|
110
|
+
- **`ask risk` ranked findings under the label `spring-audit` deduplicates**
|
|
111
|
+
(C1-23, field evaluation #9). One SEC-004 appeared three times for three call
|
|
112
|
+
sites in one class and `by_band` was an observation census under a defect
|
|
113
|
+
label — measured on BroadleafCommerce, 20 rows over 15 distinct `defect_id`
|
|
114
|
+
beside `spring-audit`'s own 15 defects / 20 findings. `build_risk` now iterates
|
|
115
|
+
`defect_identity.group_by_defect`, so one row is one thing to fix and both
|
|
116
|
+
commands report the same count (asserted against each other, not against a
|
|
117
|
+
literal). Nothing is dropped: `witness_count` and `witness_sites` carry where
|
|
118
|
+
each observation was seen, `total_findings` publishes the census under its own
|
|
119
|
+
name, and `rule_id` becomes **`rule_ids`** because one defect can be witnessed
|
|
120
|
+
by TX-001 and TX-006 at once and a singular field would have to pick one.
|
|
121
|
+
`severity` is the strongest across the witnesses, and
|
|
122
|
+
`factors.defect_severity.basis` says so.
|
|
123
|
+
|
|
124
|
+
- **A Maven build property was published as a runtime activation** (C1-22, field
|
|
125
|
+
evaluation #9 — the row it called the most expensive in its report). Two
|
|
126
|
+
`<properties>` entries setting `spring.profiles.active` made
|
|
127
|
+
`posture --resolve-environments` answer `artefacts_disagree` — *"different
|
|
128
|
+
deployments, pick one"* — on a repository where **nothing activates a profile at
|
|
129
|
+
run time**. The honest verdict, which the command already implements, is
|
|
130
|
+
`NOT DECIDED IN THIS REPOSITORY`: the default set applies and the permissive
|
|
131
|
+
profile wins, which is exactly where that audit's most severe finding lived. The
|
|
132
|
+
command turned a fail-open into a deployment ambiguity, in the scenario it is
|
|
133
|
+
sold for. A build property is now admitted as an activation only when a resource
|
|
134
|
+
in the repository consumes it (`@spring.profiles.active@`, or
|
|
135
|
+
`${spring.profiles.active}` where the build declares its resources filtered).
|
|
136
|
+
Unconsumed, the value is kept — it is still the profile set a build of it would
|
|
137
|
+
produce, so the worst case keeps ranking it — and carries the reason it decides
|
|
138
|
+
nothing, beside a `build_properties_not_consumed` count. The positive control is
|
|
139
|
+
half the battery: before this, the case where the property genuinely does decide
|
|
140
|
+
produced the identical verdict, which is how the defect stayed invisible. No
|
|
141
|
+
movement on the field repositories.
|
|
142
|
+
|
|
143
|
+
- **A rule that disappeared was reported as protection gained.** Found by the
|
|
144
|
+
negative control, not in the field: delete a security configuration and every
|
|
145
|
+
request it decided goes `permit_all` → undecided, which a two-bucket
|
|
146
|
+
opened/closed split published as *"2 requests no longer reachable
|
|
147
|
+
unauthenticated"*. Four buckets now: `opened` and `closed` (attributable to the
|
|
148
|
+
change), `no_longer_decided` (coverage lost) and `now_open_previously_undecided`
|
|
149
|
+
(our reading moved, not necessarily the system — it never turns a gate red on
|
|
150
|
+
its own). Requests undecided on either side are counted in
|
|
151
|
+
`access_not_decided_either_side`, the delta publishes `direction`, and
|
|
152
|
+
`gate.reading` states the floor (I-8).
|
|
153
|
+
- **`git_checkout` no longer offers a remedy half its callers do not have.** The
|
|
154
|
+
not-a-repository message told every caller to "pass two checkouts positionally";
|
|
155
|
+
`posture` takes none. The message states the fact and each command supplies its
|
|
156
|
+
own hint.
|
|
157
|
+
|
|
158
|
+
## [4.1.0] — 2026-08-03
|
|
159
|
+
|
|
160
|
+
Minor, not a patch, and the criterion is this project's own: a new rule can trip
|
|
161
|
+
a gate that was green yesterday. `spring-audit --ci` and `verify --fail-on any`
|
|
162
|
+
will fail on a repository that stores a credential in a deployment descriptor —
|
|
163
|
+
correctly, and for the first time. Nothing is removed, no default moves, and no
|
|
164
|
+
exit code changes meaning.
|
|
165
|
+
|
|
166
|
+
Field audit #8 (deep read-only audit of the eval #6/#7 repository, by a new
|
|
167
|
+
evaluator, 100 % catalogue coverage) ran a build self-reporting **3.5.0** while
|
|
168
|
+
4.0.1 was shipping, and re-derived twelve of its fourteen product complaints from
|
|
169
|
+
rows already closed — one of them the answer to its own critical finding. The
|
|
170
|
+
first entry below is that defect; the rest are the three findings that survived
|
|
171
|
+
verification against 4.0.1.
|
|
172
|
+
|
|
173
|
+
### Added
|
|
174
|
+
|
|
175
|
+
- **SEC-007 — a credential stored in a deployment descriptor** (CL-10). The audit
|
|
176
|
+
found `monitor:{SHA-256}dc20b820…` in
|
|
177
|
+
`src/main/options/{DPA,M3,M3DEV}/WEB-INF/web.xml`: unsalted, the same hash in all
|
|
178
|
+
three per-environment overlays, and found by hand because *"los overlays
|
|
179
|
+
`src/main/options/**` son punto ciego"*. The gap was admission surface, not
|
|
180
|
+
detection — those descriptors are files `environment_resolution`,
|
|
181
|
+
`filter_surface` and `path_filters` already open, and SEC-004 already names a
|
|
182
|
+
fast unsalted digest. The rule fires on the **shape of the value**
|
|
183
|
+
(`{ALGO}<hex>`), never on the parameter's name (VAI), with `${placeholder}` as
|
|
184
|
+
the negative control; the digest is truncated in the finding, because a report
|
|
185
|
+
about a credential must not be a place to read one from; and identical digests
|
|
186
|
+
across descriptors are composed into the statement the audit actually made —
|
|
187
|
+
three environments share one credential, so the least protected decides the
|
|
188
|
+
strength of all three. NC-002 is **narrowed, not deleted**: Helm values, CI
|
|
189
|
+
variable files and shell scripts stay declared out of scope. Zero findings across
|
|
190
|
+
six field repositories.
|
|
191
|
+
- **`ask version` says how old the build is** (C4-12). Age in days alone would not
|
|
192
|
+
have caught the audit's case — that build was three days old. On this project's
|
|
193
|
+
cadence three days is six releases (eight, in fact). So the block publishes the
|
|
194
|
+
release date, the measured release rate (2.07/day over the last 30 releases), an
|
|
195
|
+
**estimate** of releases since with its basis, and an advisory naming the upgrade
|
|
196
|
+
command and the defect ledger. It never claims a newer release exists: offline,
|
|
197
|
+
that would be the confident-wrong answer the ledger exists to prevent. `_meta`
|
|
198
|
+
carries `tool_release_date` only — the date is a constant of the build, the age
|
|
199
|
+
changes daily, and the envelope is byte-identical by contract.
|
|
200
|
+
|
|
201
|
+
### Fixed
|
|
202
|
+
|
|
203
|
+
- **`posture` now says when the container would not contradict its own resolution**
|
|
204
|
+
(CL-11). `allow-bean-definition-overriding: true` and
|
|
205
|
+
`allow-circular-references: true`, in the repository whose entire security verdict
|
|
206
|
+
turns on which `SecurityConfig` bean wins: with overriding enabled a second
|
|
207
|
+
definition replaces the first silently, so the startup failure that would have
|
|
208
|
+
caught a filter-chain override never happens. The block only ever **lowers**
|
|
209
|
+
confidence, so it sits beside the verdict and is repeated inside
|
|
210
|
+
`security.resolution_is_a_default` — the C1-20 lesson runs the other way here, and
|
|
211
|
+
a caveat a reader has to go and find does not reach the conclusion it was written
|
|
212
|
+
for. An undecidable setting is not read as off.
|
|
213
|
+
- **The platform claim matches what the battery can check** (C3-38). Third field
|
|
214
|
+
report of console mojibake on Windows/PowerShell; C3-3 and C3-12 are both recorded
|
|
215
|
+
*does not reproduce*, both scoped macOS-only. Patching a codec that was never
|
|
216
|
+
reproduced is what C3-27's rule forbids, so the README states instead that
|
|
217
|
+
Windows is a target the battery cannot verify, names what the CLI does do for it,
|
|
218
|
+
and cites the three rows. A documented limit is not a fix: C3-38 stays open until
|
|
219
|
+
a Windows runner exists.
|
|
220
|
+
|
|
221
|
+
|
|
5
222
|
## [4.0.1] — 2026-08-02
|
|
6
223
|
|
|
7
224
|
Patch, and the criterion is the one this project has used since 3.2.0: nothing here
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.2.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
|
|
@@ -97,13 +97,25 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
97
97
|
# pip / pipx
|
|
98
98
|
pipx install sourcecode # or: pip install sourcecode
|
|
99
99
|
|
|
100
|
-
ask version # ask
|
|
100
|
+
ask version # ask 4.1.0 — and, on a build that has aged,
|
|
101
|
+
# how many releases have probably shipped since
|
|
101
102
|
```
|
|
102
103
|
|
|
103
104
|
> **Package vs. command.** The install package is named `sourcecode` this release
|
|
104
105
|
> (renaming the distribution is a separate, breaking change). Installing it gives you the
|
|
105
106
|
> canonical **`ask`** command plus the deprecated **`sourcecode`** alias.
|
|
106
107
|
|
|
108
|
+
> **Platforms.** Developed and tested on macOS and Linux; every release runs its full
|
|
109
|
+
> battery there. **Windows/PowerShell is a target we cannot verify:** the CLI is written
|
|
110
|
+
> for it — UTF-8 streams are forced at both console entry points, a successful run writes
|
|
111
|
+
> nothing to stderr (PowerShell 5.1 raises `NativeCommandError` on any stderr write), and
|
|
112
|
+
> the MCP server strips the BOM PowerShell puts on stdin — but no Windows runner exists in
|
|
113
|
+
> the battery. Three separate field reports of console mojibake (`—` rendering as `â€"`)
|
|
114
|
+
> have never reproduced under measurement, and the only variable those reports share is the
|
|
115
|
+
> platform we cannot exercise (C3-3, C3-12, C3-38 in the
|
|
116
|
+
> [defect ledger](docs/DEFECT-LEDGER.md)). On Windows, prefer `--output <file>` over shell
|
|
117
|
+
> redirection, and report a rendering problem rather than assuming it is known.
|
|
118
|
+
|
|
107
119
|
---
|
|
108
120
|
|
|
109
121
|
## Quickstart
|
|
@@ -144,6 +156,10 @@ ask posture /path/to/repo --resolve-environments
|
|
|
144
156
|
# writing it, and populate .ask/contracts.yml from the measurement instead of by hand.
|
|
145
157
|
ask verify /path/to/repo --init
|
|
146
158
|
|
|
159
|
+
# Same derivation under a read-only constraint: prints what it would declare and
|
|
160
|
+
# writes nothing — the form an audit of somebody else's repository can run.
|
|
161
|
+
ask verify /path/to/repo --init --dry-run
|
|
162
|
+
|
|
147
163
|
# What each defect actually costs once reach, access and write effect are in it:
|
|
148
164
|
# severity × reachability × auth_verdict × write_effect, ordered, every factor traceable.
|
|
149
165
|
ask risk /path/to/repo
|
|
@@ -218,6 +234,9 @@ What a profile set **actually wires**: which conditional beans register, which d
|
|
|
218
234
|
`ask posture` · `ask posture --diff dev:prod` · `ask posture --property k=v`
|
|
219
235
|
→ [posture.md](docs/posture.md)
|
|
220
236
|
|
|
237
|
+
**`--diff-ref` makes it a gate instead of a report.** `--diff` moves the profile set; `--diff-ref origin/main:HEAD` (or `--since origin/main`, which compares a ref with the working tree) moves the *tree* under one profile set and answers *did this branch open an endpoint*. `--fail-on opened` exits 1 when it did. Both refs are read with `git archive` — nothing is written to the repository. The comparison is keyed on the request (`METHOD path`), counts requests only one side serves, and never reports a rule that disappeared as protection gained.
|
|
238
|
+
`ask posture . --since origin/main --fail-on opened`
|
|
239
|
+
|
|
221
240
|
**`--resolve-environments` carries it past the edge of the repository.** Every answer above
|
|
222
241
|
is conditional on a profile set, and which set runs is decided by the deployment. This flag
|
|
223
242
|
reads every artefact in the tree that can set `spring.profiles.active` — Maven/Gradle,
|
|
@@ -331,7 +350,7 @@ reading JSON sees the same boundary a buyer reads here.
|
|
|
331
350
|
| Not covered | Why | What answers it |
|
|
332
351
|
|---|---|---|
|
|
333
352
|
| **Whether request input reaches a sink — no dataflow or taint analysis.** | This engine resolves structure and wiring without compiling. Taint needs value flow through a program, which is a different analysis with a different failure mode: an unsound one produces confident findings that are wrong, and every claim here is meant to be checkable against the line that produced it. | A dataflow scanner (Semgrep, CodeQL). What this product adds on top is reachability: which of that tool's findings sit behind an endpoint that is reachable unauthenticated. |
|
|
334
|
-
| **Secrets outside Java
|
|
353
|
+
| **Secrets outside Java, Spring configuration and the deployment descriptors — a Helm value, a CI variable file, a shell script.** | The file population this analyzer reads is the Java source, the Spring configuration convention and the descriptors it already parses to resolve an environment. A secret elsewhere is not missed by a weak rule; it is outside the set of files anything here opens. Narrowed by CL-10: a credential stored in `web.xml` or a Spring XML context IS reported (SEC-007), because those files were already being read. | A dedicated secret scanner over the whole tree (gitleaks, trufflehog). |
|
|
335
354
|
| **Filter-chain order and per-filter URL patterns — the presence of a custom filter is structural only.** | Which filter runs first is decided by bean ordering this analyzer does not resolve. Where two active configurations both match a request, the answer published is `undecided` rather than a guess. | `ask posture --profile <set>` states per endpoint what the readable rules decide and what they leave undecided. |
|
|
336
355
|
| **Known vulnerabilities in dependencies — no CVE database, no version advisory matching.** | Enriching a vulnerability feed is a different product with a different update cadence; a stale embedded database is worse than no database, because it reads as a clean bill of health. | A dependency scanner (Trivy, OWASP Dependency-Check). `impact-chain` then answers which of its findings anything actually reaches. |
|
|
337
356
|
|
|
@@ -342,6 +361,13 @@ Two more boundaries worth stating in the same voice:
|
|
|
342
361
|
| **Applying a migration — nothing here edits source.** | This is the diagnosis layer: it measures what must change and what each change would reach. Rewriting code is an execution problem with an established executor, and duplicating it would mean maintaining a second, worse one. | OpenRewrite. `migrate-check` publishes the recipe each finding carries (`recipes[]`), which is the input that executor takes. |
|
|
343
362
|
| **What the process environment sets at start-up — the profile set, properties and secrets a container is given.** | Nothing in a repository can observe the environment of a process that has not started. A value read from a file here is the default the repository ships, never a guarantee of what runs. | `ask posture --resolve-environments` reads every artefact in the repository that names the profile set, says when none of them decides it, and ranks the admissible sets by what each leaves open. |
|
|
344
363
|
|
|
364
|
+
|
|
365
|
+
And one about the endpoint surface itself:
|
|
366
|
+
|
|
367
|
+
| Not covered | Why | What answers it |
|
|
368
|
+
|---|---|---|
|
|
369
|
+
| **HTTP surface that is not a Spring handler mapping — a servlet declared in `web.xml` or registered as a `ServletRegistrationBean`, a container-level mapping, a static console mounted by a library.** | The endpoint population is projected from the Spring route model: one entry per handler mapping. A servlet is reachable over HTTP and is not in that model, so it is absent from `endpoints`, from the access verdicts derived from it, and from every ratio keyed on the total. Merging a second population into that count would move a published figure and every ratio with it, so the population is declared before it is widened. | Read the deployment descriptors for `<servlet-mapping>` entries and the Spring configuration for `ServletRegistrationBean` beans. Field evaluation #9 found a monitoring console mounted this way, behind a credential `ask spring-audit` did report (SEC-007) at a path nothing here listed. |
|
|
370
|
+
|
|
345
371
|
**Positioning.** Until an executor ships, this is the **diagnosis layer**: it measures what
|
|
346
372
|
must change, what each change reaches, and what a gate should block — and it removes none of
|
|
347
373
|
it. Field evaluation scored it 7/10 as a report generator and 5.5/10 as a development tool,
|
|
@@ -59,13 +59,25 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
59
59
|
# pip / pipx
|
|
60
60
|
pipx install sourcecode # or: pip install sourcecode
|
|
61
61
|
|
|
62
|
-
ask version # ask
|
|
62
|
+
ask version # ask 4.1.0 — and, on a build that has aged,
|
|
63
|
+
# how many releases have probably shipped since
|
|
63
64
|
```
|
|
64
65
|
|
|
65
66
|
> **Package vs. command.** The install package is named `sourcecode` this release
|
|
66
67
|
> (renaming the distribution is a separate, breaking change). Installing it gives you the
|
|
67
68
|
> canonical **`ask`** command plus the deprecated **`sourcecode`** alias.
|
|
68
69
|
|
|
70
|
+
> **Platforms.** Developed and tested on macOS and Linux; every release runs its full
|
|
71
|
+
> battery there. **Windows/PowerShell is a target we cannot verify:** the CLI is written
|
|
72
|
+
> for it — UTF-8 streams are forced at both console entry points, a successful run writes
|
|
73
|
+
> nothing to stderr (PowerShell 5.1 raises `NativeCommandError` on any stderr write), and
|
|
74
|
+
> the MCP server strips the BOM PowerShell puts on stdin — but no Windows runner exists in
|
|
75
|
+
> the battery. Three separate field reports of console mojibake (`—` rendering as `â€"`)
|
|
76
|
+
> have never reproduced under measurement, and the only variable those reports share is the
|
|
77
|
+
> platform we cannot exercise (C3-3, C3-12, C3-38 in the
|
|
78
|
+
> [defect ledger](docs/DEFECT-LEDGER.md)). On Windows, prefer `--output <file>` over shell
|
|
79
|
+
> redirection, and report a rendering problem rather than assuming it is known.
|
|
80
|
+
|
|
69
81
|
---
|
|
70
82
|
|
|
71
83
|
## Quickstart
|
|
@@ -106,6 +118,10 @@ ask posture /path/to/repo --resolve-environments
|
|
|
106
118
|
# writing it, and populate .ask/contracts.yml from the measurement instead of by hand.
|
|
107
119
|
ask verify /path/to/repo --init
|
|
108
120
|
|
|
121
|
+
# Same derivation under a read-only constraint: prints what it would declare and
|
|
122
|
+
# writes nothing — the form an audit of somebody else's repository can run.
|
|
123
|
+
ask verify /path/to/repo --init --dry-run
|
|
124
|
+
|
|
109
125
|
# What each defect actually costs once reach, access and write effect are in it:
|
|
110
126
|
# severity × reachability × auth_verdict × write_effect, ordered, every factor traceable.
|
|
111
127
|
ask risk /path/to/repo
|
|
@@ -180,6 +196,9 @@ What a profile set **actually wires**: which conditional beans register, which d
|
|
|
180
196
|
`ask posture` · `ask posture --diff dev:prod` · `ask posture --property k=v`
|
|
181
197
|
→ [posture.md](docs/posture.md)
|
|
182
198
|
|
|
199
|
+
**`--diff-ref` makes it a gate instead of a report.** `--diff` moves the profile set; `--diff-ref origin/main:HEAD` (or `--since origin/main`, which compares a ref with the working tree) moves the *tree* under one profile set and answers *did this branch open an endpoint*. `--fail-on opened` exits 1 when it did. Both refs are read with `git archive` — nothing is written to the repository. The comparison is keyed on the request (`METHOD path`), counts requests only one side serves, and never reports a rule that disappeared as protection gained.
|
|
200
|
+
`ask posture . --since origin/main --fail-on opened`
|
|
201
|
+
|
|
183
202
|
**`--resolve-environments` carries it past the edge of the repository.** Every answer above
|
|
184
203
|
is conditional on a profile set, and which set runs is decided by the deployment. This flag
|
|
185
204
|
reads every artefact in the tree that can set `spring.profiles.active` — Maven/Gradle,
|
|
@@ -293,7 +312,7 @@ reading JSON sees the same boundary a buyer reads here.
|
|
|
293
312
|
| Not covered | Why | What answers it |
|
|
294
313
|
|---|---|---|
|
|
295
314
|
| **Whether request input reaches a sink — no dataflow or taint analysis.** | This engine resolves structure and wiring without compiling. Taint needs value flow through a program, which is a different analysis with a different failure mode: an unsound one produces confident findings that are wrong, and every claim here is meant to be checkable against the line that produced it. | A dataflow scanner (Semgrep, CodeQL). What this product adds on top is reachability: which of that tool's findings sit behind an endpoint that is reachable unauthenticated. |
|
|
296
|
-
| **Secrets outside Java
|
|
315
|
+
| **Secrets outside Java, Spring configuration and the deployment descriptors — a Helm value, a CI variable file, a shell script.** | The file population this analyzer reads is the Java source, the Spring configuration convention and the descriptors it already parses to resolve an environment. A secret elsewhere is not missed by a weak rule; it is outside the set of files anything here opens. Narrowed by CL-10: a credential stored in `web.xml` or a Spring XML context IS reported (SEC-007), because those files were already being read. | A dedicated secret scanner over the whole tree (gitleaks, trufflehog). |
|
|
297
316
|
| **Filter-chain order and per-filter URL patterns — the presence of a custom filter is structural only.** | Which filter runs first is decided by bean ordering this analyzer does not resolve. Where two active configurations both match a request, the answer published is `undecided` rather than a guess. | `ask posture --profile <set>` states per endpoint what the readable rules decide and what they leave undecided. |
|
|
298
317
|
| **Known vulnerabilities in dependencies — no CVE database, no version advisory matching.** | Enriching a vulnerability feed is a different product with a different update cadence; a stale embedded database is worse than no database, because it reads as a clean bill of health. | A dependency scanner (Trivy, OWASP Dependency-Check). `impact-chain` then answers which of its findings anything actually reaches. |
|
|
299
318
|
|
|
@@ -304,6 +323,13 @@ Two more boundaries worth stating in the same voice:
|
|
|
304
323
|
| **Applying a migration — nothing here edits source.** | This is the diagnosis layer: it measures what must change and what each change would reach. Rewriting code is an execution problem with an established executor, and duplicating it would mean maintaining a second, worse one. | OpenRewrite. `migrate-check` publishes the recipe each finding carries (`recipes[]`), which is the input that executor takes. |
|
|
305
324
|
| **What the process environment sets at start-up — the profile set, properties and secrets a container is given.** | Nothing in a repository can observe the environment of a process that has not started. A value read from a file here is the default the repository ships, never a guarantee of what runs. | `ask posture --resolve-environments` reads every artefact in the repository that names the profile set, says when none of them decides it, and ranks the admissible sets by what each leaves open. |
|
|
306
325
|
|
|
326
|
+
|
|
327
|
+
And one about the endpoint surface itself:
|
|
328
|
+
|
|
329
|
+
| Not covered | Why | What answers it |
|
|
330
|
+
|---|---|---|
|
|
331
|
+
| **HTTP surface that is not a Spring handler mapping — a servlet declared in `web.xml` or registered as a `ServletRegistrationBean`, a container-level mapping, a static console mounted by a library.** | The endpoint population is projected from the Spring route model: one entry per handler mapping. A servlet is reachable over HTTP and is not in that model, so it is absent from `endpoints`, from the access verdicts derived from it, and from every ratio keyed on the total. Merging a second population into that count would move a published figure and every ratio with it, so the population is declared before it is widened. | Read the deployment descriptors for `<servlet-mapping>` entries and the Spring configuration for `ServletRegistrationBean` beans. Field evaluation #9 found a monitoring console mounted this way, behind a credential `ask spring-audit` did report (SEC-007) at a path nothing here listed. |
|
|
332
|
+
|
|
307
333
|
**Positioning.** Until an executor ships, this is the **diagnosis layer**: it measures what
|
|
308
334
|
must change, what each change reaches, and what a gate should block — and it removes none of
|
|
309
335
|
it. Field evaluation scored it 7/10 as a report generator and 5.5/10 as a development tool,
|
|
@@ -127,13 +127,21 @@ def bfs_callers(
|
|
|
127
127
|
max_depth: int,
|
|
128
128
|
impl_graph: object | None = None,
|
|
129
129
|
method_scoped: bool = False,
|
|
130
|
-
) -> tuple[list[str], list[str], bool, int]:
|
|
130
|
+
) -> tuple[list[str], list[str], bool, int, int]:
|
|
131
131
|
"""Walk the reverse graph from *seed_fqns*.
|
|
132
132
|
|
|
133
|
-
Returns ``(direct, indirect, was_truncated, self_excluded)``.
|
|
134
|
-
depth 1 — the callers of the seeds themselves; ``indirect`` is
|
|
135
|
-
beyond, up to *max_depth*. ``was_truncated`` says the hub-class
|
|
136
|
-
the walk, which makes the result a floor.
|
|
133
|
+
Returns ``(direct, indirect, was_truncated, self_excluded, guard_population)``.
|
|
134
|
+
``direct`` is depth 1 — the callers of the seeds themselves; ``indirect`` is
|
|
135
|
+
depth 2 and beyond, up to *max_depth*. ``was_truncated`` says the hub-class
|
|
136
|
+
guard capped the walk, which makes the result a floor.
|
|
137
|
+
|
|
138
|
+
``guard_population`` is the number the guard actually read: unique caller
|
|
139
|
+
**reference sites**, method-level keys included, before anything is
|
|
140
|
+
normalised to a class. It is returned rather than left implicit because the
|
|
141
|
+
figure the payload publishes is the class count, and a message that names
|
|
142
|
+
the cap without naming its own population contradicts the payload beside it —
|
|
143
|
+
measured on openmrs-core, *"symbol has > 500 direct callers"* printed next to
|
|
144
|
+
`direct_caller_count: 215` (C2-21).
|
|
137
145
|
|
|
138
146
|
A class's own members are members, not external callers, so they are dropped
|
|
139
147
|
and counted (BUG #2 — leaving them in inflated a blast radius ~12×). For a
|
|
@@ -170,7 +178,8 @@ def bfs_callers(
|
|
|
170
178
|
if etype not in SKIP_EDGE_TYPES:
|
|
171
179
|
unique_direct.update(c for c in fqn_list if not _is_self_reference(c))
|
|
172
180
|
|
|
173
|
-
|
|
181
|
+
guard_population = len(unique_direct)
|
|
182
|
+
effective_depth = 1 if guard_population > CALLER_CAP else max_depth
|
|
174
183
|
if effective_depth < max_depth:
|
|
175
184
|
was_truncated = True
|
|
176
185
|
|
|
@@ -202,4 +211,4 @@ def bfs_callers(
|
|
|
202
211
|
else:
|
|
203
212
|
_add_caller(caller, depth)
|
|
204
213
|
|
|
205
|
-
return direct, indirect, was_truncated, self_excluded
|
|
214
|
+
return direct, indirect, was_truncated, self_excluded, guard_population
|
|
@@ -115,6 +115,30 @@ class CanonicalSecurity:
|
|
|
115
115
|
# CanonicalEndpoint
|
|
116
116
|
# ---------------------------------------------------------------------------
|
|
117
117
|
|
|
118
|
+
def request_identity(method: str, path: str) -> str:
|
|
119
|
+
"""The identity of the **request**, not of the handler that serves it.
|
|
120
|
+
|
|
121
|
+
An endpoint `id` identifies a handler: it carries the controller FQN and the
|
|
122
|
+
handler symbol, so moving a mapping to another class produces a different
|
|
123
|
+
`id` for the same URL. That is the right identity inside one tree and the
|
|
124
|
+
wrong one across two — a comparison keyed on it reports every refactored
|
|
125
|
+
handler as one endpoint removed and another added, and the reader asking
|
|
126
|
+
"did this change what a caller can reach" has to subtract the noise by hand.
|
|
127
|
+
|
|
128
|
+
Two states of one repository — `contract-diff` between refs, `posture
|
|
129
|
+
--diff-ref` — compare what a client can call, which is METHOD + path. One
|
|
130
|
+
authority for it, because two spellings of one identity is how two commands
|
|
131
|
+
come to disagree about the same endpoint (ADR-0008 R11).
|
|
132
|
+
|
|
133
|
+
Note what this deliberately does *not* do: it does not normalise path
|
|
134
|
+
variable names or inline regexes. `/{id:.*}` and `/{id:[^0-9].*}` are
|
|
135
|
+
different URLs to a client, and equating them would hide a real change in
|
|
136
|
+
the surface — measured on a fleet repository where exactly that edit moved
|
|
137
|
+
93 requests.
|
|
138
|
+
"""
|
|
139
|
+
return f"{str(method).upper()} {path}"
|
|
140
|
+
|
|
141
|
+
|
|
118
142
|
@dataclass
|
|
119
143
|
class CanonicalEndpoint:
|
|
120
144
|
"""Canonical endpoint entity — single source of truth for REST endpoint data.
|
|
@@ -159,6 +183,11 @@ class CanonicalEndpoint:
|
|
|
159
183
|
"""Deterministic endpoint ID — stable across formatting/body changes."""
|
|
160
184
|
return f"{method}:{path}:{controller_class}:{handler_symbol}"
|
|
161
185
|
|
|
186
|
+
@property
|
|
187
|
+
def request_key(self) -> str:
|
|
188
|
+
"""This endpoint's request identity — see `request_identity`."""
|
|
189
|
+
return request_identity(self.method, self.path)
|
|
190
|
+
|
|
162
191
|
|
|
163
192
|
# ---------------------------------------------------------------------------
|
|
164
193
|
# CanonicalRepositoryIR
|