cg-code-graph 0.21.0__tar.gz → 0.21.1__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.
- {cg_code_graph-0.21.0/cg_code_graph.egg-info → cg_code_graph-0.21.1}/PKG-INFO +1 -1
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/__init__.py +1 -1
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/redact.py +46 -21
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/laravel/plugin.py +1 -1
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/surface.py +2 -2
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1/cg_code_graph.egg-info}/PKG-INFO +1 -1
- cg_code_graph-0.21.1/tests/test_redact_salt.py +235 -0
- cg_code_graph-0.21.0/tests/test_redact_salt.py +0 -97
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/LICENSE +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/MANIFEST.in +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/README.md +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/__main__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/affected.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/agent_rules.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/agents.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/aitools.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/android_ipc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/apple_ipc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/apps.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/blindspots.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/bridges.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/brokers.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/cli.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/concepts.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/config.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/cache.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/detect.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/env.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/extractors.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/fsutil.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/generated.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/model.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/paths.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/store.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/core/syntax_errors.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/coverage.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/dart_isolates.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/doctor.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/explore.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/external.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/graphql.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/guard_facts.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/hooks.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/indexer.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/insecure_transport.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/jobs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/link.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/lint_async.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/local_ipc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/local_sockets.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/mcp_install.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/mcp_server.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/parity.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/parity_structure.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/payload.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plans.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/platform_scan.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/platforms.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/astro/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/astro/content.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/astro/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/cfamily/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/cfamily/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/cfamily/syntax.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/bridges.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/extractor/bin/extract.dart +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/extractor/pubspec.lock +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/extractor/pubspec.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/http.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/models.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/dart/program.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/django/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/django/extras.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/django/models.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/django/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/django/schemas.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/django/shapes.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/django/urls.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/express/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/express/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/flutter/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/flutter/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/java/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/java/exact.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/java/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/jvm/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/jvm/interop.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/jvm/spring.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/kotlin/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/kotlin/exact.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/kotlin/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/kotlin/reparse.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/laravel/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/laravel/broadcast.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/laravel/tests.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/laravel/values.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/native/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/native/gates.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/native/runner.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/native/scipread.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/native/ts.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/nest/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/nest/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/nextjs/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/nextjs/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/nuxt/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/nuxt/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/external.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/extractor/composer.json +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/extractor/composer.lock +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/extractor/extract.php +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/gating.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/http_client.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/inline_guards.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/php/strings.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/aitools.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/external.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/fields.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/refs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/roots.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/socketio.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/subproc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/tests.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/python/values.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/pyweb/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/pyweb/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/pyweb/values.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/rust/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/rust/cargo.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/rust/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/rust/syntax.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/scip/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/scip/importer.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/scip/scip.proto +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/scip/scip_pb2.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/stubs/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/stubs/plugins.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/swift/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/swift/baseurl.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/swift/exact.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/swift/indexstore.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/swift/packages.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/swift/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/baseurl.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/extractor/extract.mjs +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/extractor/fw.mjs +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/extractor/package-lock.json +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/extractor/package.json +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/extractor/rr.mjs +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/nav.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/plugin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/ts/react_router.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/tsweb/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/tsweb/common.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/plugins/tsweb/data.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/c_cpp.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/common.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/dart.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/django-ninja.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/django.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/djangorestframework.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/express.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/java.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/kotlin.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/laravel.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/nest.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/nextjs.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/nuxt.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/php.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/python.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/react-router.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/rust.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/spring.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/swift.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/presets/typescript.yaml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/process_runs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/protocols/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/protocols/builtin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/protocols/matchers.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/protocols/view.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/query.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/realtime.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/realtime_events.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/realtime_native.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/realtime_ws.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/roundtrip.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/routes.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/rpc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/sdk_systems.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/sockets.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/ssrf_input.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/starters.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/tests_index.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/udp_apps.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/__init__.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/graph.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/server.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/app.css +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/app.js +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/index.html +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/layered.js +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/vendor/VERSIONS.txt +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/vendor/cose-base.js +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/vendor/cytoscape-fcose.js +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/vendor/cytoscape.min.js +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/static/vendor/layout-base.js +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/tools/package-lock.json +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/tools/package.json +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/viz/tools/shoot.mjs +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/watch.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/webhook_subs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/webhooks.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph/xcode.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph.egg-info/SOURCES.txt +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph.egg-info/dependency_links.txt +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph.egg-info/entry_points.txt +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph.egg-info/requires.txt +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/cg_code_graph.egg-info/top_level.txt +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/pyproject.toml +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/setup.cfg +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_affected.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_agents_install.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_agents_md.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ai_tools.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_android_ipc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_apple_ipc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_astro.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_astro_content.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_astro_server.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_bridges.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_bridges_pigeon_monorepo.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_brokers.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_cache_clean.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_cache_invalidation.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_cache_migration.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_channels_tests.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_cli_output.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_completeness.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_config_extras.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_coverage.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_coverage_ts_hint.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_dart_isolates.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_dispatch_interfaces.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_django_flutter.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_docs_languages.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_docs_links.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_echo_ref.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_echo_wrapper.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_elysia.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_enum_constants.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_env.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_explore.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_external.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_fp_batch.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_gating.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_generated.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_graphql.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_hooks.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_impact_overrides.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_index_isolation.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_inherited_overrides.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_inline_guards.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_java.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_java_exact.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_jobs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_js_refs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_js_runtime.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_kotlin.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_kotlin_binding.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_kotlin_exact.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_kotlin_fields.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_kotlin_mixed.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_kotlin_nav_consts.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_kotlin_props.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_laravel_determinism.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_laravel_resources.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_lint_async.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_local_ipc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_local_sockets.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_mcp.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_mcp_index.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_mcp_install.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_native_cfamily.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_native_concurrency.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_native_rust.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_nuxt_import_dirs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_nuxt_rootsrc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_packaging.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_parity.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_parity_structure.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_php_http.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_php_props.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_plain_js.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_plans.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_platform_gaps.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_platform_type_variants.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_platforms.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_platforms_apple.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_presets_config.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_process_ipc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_process_runs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_protocols.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_app_tests_module.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_collection_chains.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_compat.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_entry_refs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_fields.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_infer_cycles.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_roots.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_subprocess_tests.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_tests.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_web_hosts.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_web_routes.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_python_web_routes_more.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_react_router.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_realtime.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_realtime_native.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_request_keys.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_roundtrip.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_route_tables.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_routes_search_explain.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_rpc.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_scip_health.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_sdk_external.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_sdk_external_infra.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_sdk_external_mail.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_snippet.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_sockets.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_spring.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ssrf.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_starters_budget.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_surface.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_surface_transport.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_binding.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_exact.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_facts.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_fields.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_modules.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_more.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_nav_values.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_parse_errors.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_props.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_sdk.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_testing.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_swift_values.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_tests_scope.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ts_fields.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ts_fields_js_vue.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ts_file_specs.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ts_frameworks.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ts_node_http.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ts_nuxt.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_ts_src_tsconfig.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_unguarded.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_values.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_viz.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_vue_nav.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_watch.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_webhooks.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_webhooks_152.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_webhooks_152b.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_websockets.py +0 -0
- {cg_code_graph-0.21.0 → cg_code_graph-0.21.1}/tests/test_workspace_link.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""code-graph: deterministic code knowledge graph (beta)."""
|
|
2
|
-
__version__ = "0.21.
|
|
2
|
+
__version__ = "0.21.1"
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Secret values never reach the graph database (#47).
|
|
2
2
|
|
|
3
|
-
A literal that belongs to a credential-looking key (password, secret,
|
|
4
|
-
|
|
3
|
+
A literal that belongs to a credential-looking key (its last word is password, secret, dsn, ..., or an
|
|
4
|
+
explicit compound such as api_key; a bare `key` / `token` counts unless a known non-secret word precedes it
|
|
5
|
+
(`primary_key`, `cache.key`), whatever the value looks like; `AUTH_GUARD` or `..._TOKEN_TABLE`
|
|
6
|
+
do not count) is stored as `redacted:hmac:<first 8 hex of HMAC-SHA-256(graph salt, value)>`: enough to see that
|
|
5
7
|
a literal exists and to tell two values apart within one graph, not enough to recover one, and not recognisable by
|
|
6
8
|
hashing a common password. The salt is `secrets.token_hex(16)`, created on the first index of a graph DB, kept in its
|
|
7
9
|
`meta` table (`redact_salt`) and carried over by a re-index or refresh of that DB; a new DB gets a new salt, so markers
|
|
@@ -20,29 +22,52 @@ import secrets
|
|
|
20
22
|
|
|
21
23
|
MARK = "redacted:hmac:"
|
|
22
24
|
LEGACY_MARK = "redacted:sha256:"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
_SECRET_LAST = {"password", "passwd", "pwd", "pass", "passphrase", "secret", "dsn"}
|
|
26
|
+
_SECRET_COMPOUNDS = {("api", "key"), ("apikey",), ("access", "key"), ("secret", "key"), ("private", "key"),
|
|
27
|
+
("signing", "key"), ("encryption", "key"), ("client", "secret"), ("webhook", "secret"),
|
|
28
|
+
("auth", "token"), ("api", "token"), ("access", "token"), ("refresh", "token"), ("bearer", "token")}
|
|
29
|
+
_BARE_LAST = {"key", "token"}
|
|
30
|
+
SECRET_WORDS = tuple(sorted({w.upper() for w in _SECRET_LAST} | {"_".join(c).upper() for c in _SECRET_COMPOUNDS}
|
|
31
|
+
| {w.upper() for w in _BARE_LAST}))
|
|
32
|
+
_NON_SECRET_BEFORE = {"primary", "foreign", "sort", "partition", "route", "cache", "idempotency", "remember", "csrf",
|
|
33
|
+
"xsrf", "public", "i18n", "translation", "lookup", "unique", "index", "storage", "prefix"}
|
|
27
34
|
_URL_PW = re.compile(r"(?P<head>\b[A-Za-z][\w+.-]*://[^/\s:@'\"]*:)(?P<pw>[^@/\s'\"]+)(?P<tail>@)")
|
|
28
35
|
_PLACEHOLDER = ("${", "{{", "%(", "<", "$")
|
|
29
36
|
|
|
30
37
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
def _words(segment: str) -> list[str]:
|
|
39
|
+
spaced = re.sub(r"([a-z])([A-Z])", r"\1_\2", segment)
|
|
40
|
+
return [w for w in re.split(r"[^A-Za-z0-9]+", spaced.lower()) if w]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def secret_key(key: str, value=None) -> bool:
|
|
44
|
+
"""Whether a config key / env name holds a credential, from the words of the name (split on `.`, `_`, `-` and
|
|
45
|
+
camelCase). A credential when the last word is password, passwd, pwd, pass, passphrase, secret or dsn, when the
|
|
46
|
+
name ends in an explicit compound (api_key, access_key, signing_key, client_secret, auth_token, refresh_token, ...),
|
|
47
|
+
or for Laravel's `app.key` / `APP_KEY`. A bare `key` or `token` as last word is a credential by default, as it
|
|
48
|
+
was in 0.21.0, and is kept only when the word before it (a dotted segment counts) is a known non-secret one
|
|
49
|
+
(primary, foreign, sort, partition, route, cache, idempotency, remember, csrf, xsrf, public, i18n, translation,
|
|
50
|
+
lookup, unique, index, storage, prefix). The value never decides: a token can be any shape (lowercase words,
|
|
51
|
+
dotted, `key-` prefixed), so `value` is accepted for the callers' signature only. A credential word earlier in the name never counts
|
|
52
|
+
(`AUTH_GUARD`, `AUTH_PASSWORD_BROKER`, `AUTH_PASSWORD_RESET_TOKEN_TABLE`, `try_it_credentials_policy`), and
|
|
53
|
+
`*_PUBLIC_KEY`, `*_KEY_ID`, `*_KEY_PATH`, `*_KEY_FILE` are not credentials."""
|
|
54
|
+
key = str(key)
|
|
55
|
+
words = _words(key.rsplit(".", 1)[-1])
|
|
56
|
+
if not words:
|
|
57
|
+
return False
|
|
58
|
+
all_words = _words(key)
|
|
59
|
+
if all_words == ["app", "key"]:
|
|
40
60
|
return True
|
|
41
|
-
|
|
42
|
-
words = [w for w in re.split(r"[^A-Za-z0-9]+", spaced.upper()) if w]
|
|
43
|
-
if any(w in _EXTRA_WORDS for w in words):
|
|
61
|
+
if words[-1] in _SECRET_LAST or any(tuple(words[-n:]) in _SECRET_COMPOUNDS for n in (1, 2) if len(words) >= n):
|
|
44
62
|
return True
|
|
45
|
-
|
|
63
|
+
if words[-1] not in _BARE_LAST:
|
|
64
|
+
return False
|
|
65
|
+
if len(all_words) > 1 and all_words[-2] in _NON_SECRET_BEFORE:
|
|
66
|
+
return False
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
looks_secret = secret_key
|
|
46
71
|
|
|
47
72
|
|
|
48
73
|
def is_redacted(v) -> bool:
|
|
@@ -79,7 +104,7 @@ def redact(value, key: str | None, salt: str):
|
|
|
79
104
|
by one, anything else unchanged. Non-strings and placeholders pass through."""
|
|
80
105
|
if not isinstance(value, str):
|
|
81
106
|
return value
|
|
82
|
-
if key is not None and looks_secret(key) and not placeholder(value):
|
|
107
|
+
if key is not None and looks_secret(key, value) and not placeholder(value):
|
|
83
108
|
return marker(value, salt)
|
|
84
109
|
return redact_url(value, salt)
|
|
85
110
|
|
|
@@ -100,7 +125,7 @@ def _holds_secret(d: dict, k: str) -> bool:
|
|
|
100
125
|
if k.lower() in _DIRECT:
|
|
101
126
|
return not _REFERENCE.match(str(d[k]))
|
|
102
127
|
if k in _VALUE_KEYS:
|
|
103
|
-
return any(isinstance(d.get(nk), str) and looks_secret(d[nk]) for nk in _NAME_KEYS)
|
|
128
|
+
return any(isinstance(d.get(nk), str) and looks_secret(d[nk], d.get(k)) for nk in _NAME_KEYS)
|
|
104
129
|
return False
|
|
105
130
|
|
|
106
131
|
|
|
@@ -216,7 +216,7 @@ class LaravelPlugin(FrameworkPlugin):
|
|
|
216
216
|
attrs["value"] = redact(n["literal"], n["key"], self.b.redact_salt)
|
|
217
217
|
if n.get("envs"):
|
|
218
218
|
attrs["env"] = [e["env"] for e in n["envs"]]
|
|
219
|
-
attrs["env_default"] = [redact(e["default"], e["env"] if looks_secret(e["env"]) else n["key"], self.b.redact_salt)
|
|
219
|
+
attrs["env_default"] = [redact(e["default"], e["env"] if looks_secret(e["env"], e["default"]) else n["key"], self.b.redact_salt)
|
|
220
220
|
for e in n["envs"]]
|
|
221
221
|
cid = b.add_node("config", key, file=f, line=n["line"], module="config", lang="php", attrs=attrs)
|
|
222
222
|
self.config_keys[key] = n
|
|
@@ -306,13 +306,13 @@ def _config_literals(ctx: Ctx) -> list[dict]:
|
|
|
306
306
|
key = r["name"] or r["id"][len("config:"):]
|
|
307
307
|
last = key.split(".")[-1]
|
|
308
308
|
repo = ctx.repo_of(a, r["file"])
|
|
309
|
-
if secret_key(last) and not _placeholder(a.get("value")):
|
|
309
|
+
if secret_key(last, a.get("value")) and not _placeholder(a.get("value")):
|
|
310
310
|
out.append(make_finding(ctx, "hardcoded", protocol="config", node=r["id"], file=r["file"], line=r["line"],
|
|
311
311
|
confidence="resolved", repo=repo, attrs=a, see=f"cg node '{r['id']}'",
|
|
312
312
|
entry_ids=[r["id"]], detail=f"literal {_kind_of_key(last)} in config key {key} (value not shown)"))
|
|
313
313
|
envs, defaults = a.get("env") or [], a.get("env_default") or []
|
|
314
314
|
for env, dflt in zip(envs, defaults):
|
|
315
|
-
if secret_key(str(env)) and not _placeholder(dflt):
|
|
315
|
+
if secret_key(str(env), dflt) and not _placeholder(dflt):
|
|
316
316
|
out.append(make_finding(ctx, "hardcoded", protocol="config", node=r["id"], file=r["file"], line=r["line"],
|
|
317
317
|
confidence="heuristic", repo=repo, attrs=a, see=f"cg node '{r['id']}'",
|
|
318
318
|
entry_ids=[r["id"]], detail=f"literal default for env {env} in config key {key} (value not shown)"))
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Salted secret fingerprints (#47 part 2d): `redacted:hmac:<8 hex>` is an HMAC-SHA-256 keyed with a per-graph salt kept
|
|
2
|
+
in the `meta` table (`redact_salt`), so a common password is not recognisable from its marker and markers only compare
|
|
3
|
+
within one graph (and across re-indexes of it)."""
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
import shutil
|
|
7
|
+
import sqlite3
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
14
|
+
sys.path.insert(0, str(ROOT))
|
|
15
|
+
from cg_code_graph.core import redact as R # noqa: E402
|
|
16
|
+
from cg_code_graph.indexer import index_project # noqa: E402
|
|
17
|
+
|
|
18
|
+
PHP_FIX = ROOT / "tests" / "surface_fixture" / "php"
|
|
19
|
+
needs_php = pytest.mark.skipif(shutil.which("php") is None, reason="php is not installed (PHP extractor)")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_marker_is_keyed_not_a_plain_digest():
|
|
23
|
+
plain = hashlib.sha256(b"password").hexdigest()[:8]
|
|
24
|
+
assert plain == "5e884898"
|
|
25
|
+
m = R.marker("password", "salt-a-0000")
|
|
26
|
+
assert m.startswith(R.MARK) and m != R.MARK + plain and m != R.LEGACY_MARK + plain
|
|
27
|
+
assert plain not in m
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_two_graphs_give_different_markers_and_one_graph_a_stable_one():
|
|
31
|
+
assert R.marker("hunter2", "salt-a") != R.marker("hunter2", "salt-b")
|
|
32
|
+
assert R.marker("hunter2", "salt-a") == R.marker("hunter2", "salt-a")
|
|
33
|
+
assert R.marker("hunter2", "salt-a") != R.marker("hunter3", "salt-a")
|
|
34
|
+
with pytest.raises(ValueError):
|
|
35
|
+
R.marker("hunter2", "")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_redact_and_urls_use_the_salt_and_both_prefixes_count_as_redacted():
|
|
39
|
+
assert R.redact("fake-bookstore-pw-0000", "db_password", "s1") == R.marker("fake-bookstore-pw-0000", "s1")
|
|
40
|
+
url = R.redact("mysql://reader:fake-bookstore-pw-0000@replica.example.test/shop", None, "s1")
|
|
41
|
+
assert url == f"mysql://reader:{R.marker('fake-bookstore-pw-0000', 's1')}@replica.example.test/shop"
|
|
42
|
+
assert R.is_redacted(R.marker("x", "s1")) and R.is_redacted("redacted:sha256:5e884898")
|
|
43
|
+
assert R.redact(R.marker("x", "s1"), "password", "s2") == R.marker("x", "s1") # a marker is never re-hashed
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _config(db):
|
|
47
|
+
con = sqlite3.connect(db)
|
|
48
|
+
try:
|
|
49
|
+
salt = json.loads(con.execute("SELECT value FROM meta WHERE key='redact_salt'").fetchone()[0])
|
|
50
|
+
attrs = {r[0]: json.loads(r[1] or "{}") for r in con.execute("SELECT id, attrs FROM nodes WHERE kind='config'")}
|
|
51
|
+
finally:
|
|
52
|
+
con.close()
|
|
53
|
+
return salt, attrs["config:services.github.secret"]["value"]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@needs_php
|
|
57
|
+
def test_salt_is_created_per_graph_and_kept_on_reindex(tmp_path):
|
|
58
|
+
a, b = tmp_path / "a.db", tmp_path / "b.db"
|
|
59
|
+
index_project(PHP_FIX, a, "php")
|
|
60
|
+
index_project(PHP_FIX, b, "php")
|
|
61
|
+
salt_a, marker_a = _config(a)
|
|
62
|
+
salt_b, marker_b = _config(b)
|
|
63
|
+
assert len(salt_a) == 32 and salt_a != salt_b
|
|
64
|
+
assert marker_a.startswith("redacted:hmac:") and marker_a != marker_b # two graphs, same value
|
|
65
|
+
index_project(PHP_FIX, a, "php") # re-index the same DB
|
|
66
|
+
assert _config(a) == (salt_a, marker_a)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@needs_php
|
|
70
|
+
def test_refresh_keeps_the_salt(tmp_path):
|
|
71
|
+
from cg_code_graph import hooks
|
|
72
|
+
root = tmp_path / "php"
|
|
73
|
+
shutil.copytree(PHP_FIX, root, ignore=shutil.ignore_patterns("vendor"))
|
|
74
|
+
db = tmp_path / "graph.db"
|
|
75
|
+
index_project(root, db, "php")
|
|
76
|
+
before = _config(db)
|
|
77
|
+
(root / "app" / "Services" / "Extra.php").write_text("<?php\nnamespace App\\Services;\nclass Extra {}\n")
|
|
78
|
+
assert hooks.refresh(str(root), str(db), "php", quiet=True) == 0
|
|
79
|
+
assert _config(db) == before
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@needs_php
|
|
83
|
+
def test_cg_link_keeps_markers_and_has_no_salt_of_its_own(tmp_path):
|
|
84
|
+
from cg_code_graph.link import link_many
|
|
85
|
+
a, b, out = tmp_path / "a.db", tmp_path / "b.db", tmp_path / "combined.db"
|
|
86
|
+
index_project(PHP_FIX, a, "php-a")
|
|
87
|
+
index_project(PHP_FIX, b, "php-b")
|
|
88
|
+
link_many([("one", str(a), "both"), ("two", str(b), "both")], str(out))
|
|
89
|
+
con = sqlite3.connect(out)
|
|
90
|
+
try:
|
|
91
|
+
assert con.execute("SELECT 1 FROM meta WHERE key='redact_salt'").fetchone() is None
|
|
92
|
+
vals = {r[0]: json.loads(r[1] or "{}").get("value") for r in con.execute(
|
|
93
|
+
"SELECT id, attrs FROM nodes WHERE id LIKE '%config:services.github.secret'")}
|
|
94
|
+
finally:
|
|
95
|
+
con.close()
|
|
96
|
+
assert len(vals) == 2 and all(str(v).startswith("redacted:hmac:") for v in vals.values())
|
|
97
|
+
assert len(set(vals.values())) == 2 # fingerprints of different source graphs do not compare
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
LARAVEL_FIX = ROOT / "tests" / "redact_fixture" / "laravel-bookstore"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.parametrize("key", ["AUTH_GUARD", "AUTH_PASSWORD_BROKER", "AUTH_PASSWORD_RESET_TOKEN_TABLE",
|
|
104
|
+
"try_it_credentials_policy", "auth.defaults.guard", "auth.passwords.readers.table",
|
|
105
|
+
"DB_PASSWORD_TABLE", "S3_PUBLIC_KEY", "SIGNING_KEY_ID", "SESSION_DRIVER", "tokenTtl",
|
|
106
|
+
"REPORTS_KEY_PATH", "primary_key", "foreign_key", "sort_key", "cache.key", "route_key",
|
|
107
|
+
"partition_key", "idempotency_key", "remember_token"])
|
|
108
|
+
def test_last_word_rule_leaves_framework_settings_alone(key):
|
|
109
|
+
assert not R.looks_secret(key, "created_at") and not R.secret_key(key, "created_at")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@pytest.mark.parametrize("key", ["AUTH_TOKEN", "DB_PASSWORD", "STRIPE_SECRET", "password", "client_secret", "api_key",
|
|
113
|
+
"apiKey", "REPORTS_DSN", "private_key", "signing_key", "ENCRYPTION_KEY",
|
|
114
|
+
"webhook_secret", "access_token", "refresh_token", "bearer_token", "DB_PASS",
|
|
115
|
+
"app.key", "APP_KEY", "STRIPE_SECRET_KEY"])
|
|
116
|
+
def test_last_word_rule_keeps_credentials(key):
|
|
117
|
+
assert R.looks_secret(key) and R.secret_key(key)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
_HEX32 = "a" + ("fake" + "0a1b" * 7)[:31] # 32 chars, lowercase hex-looking, starts with a letter
|
|
121
|
+
_REDACTED_ROWS = [
|
|
122
|
+
("LEDGER_TOKEN", "fake-bookstore-token-0000"),
|
|
123
|
+
("LEDGER_TOKEN", "SURF-PHP-LEDGER-DEFAULT-d7e2"),
|
|
124
|
+
("services.ledger.token", _HEX32),
|
|
125
|
+
("MAILGUN_KEY", "key-" + "fake0a1b" * 3 + "fake0a"),
|
|
126
|
+
("PUSHER_APP_KEY", "fakebookstore" + "abcdefghijk"[:9] + "0042"),
|
|
127
|
+
("services.x.key", "FakeBook" + "Store12345" + "AbC"),
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@pytest.mark.parametrize("key,value", _REDACTED_ROWS)
|
|
132
|
+
def test_bare_key_and_token_with_credential_looking_values_are_redacted(key, value):
|
|
133
|
+
assert R.secret_key(key, value) and R.looks_secret(key, value)
|
|
134
|
+
assert R.redact(value, key, "s1").startswith(R.MARK)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
_HOSTILE_ROWS = [
|
|
138
|
+
"key-abcdef", "key-fakebookstorekeynodigits", "key-FAKE", # key- prefixed, with and without digits
|
|
139
|
+
"abc.def.ghi", "fake.bookstore.token", # dotted, JWT-segment-like
|
|
140
|
+
"eyJmYWtlIjoiYm9va3N0b3JlIn0", "FakeBookstoreTokenNoDigits", # base64url segment, mixed case no digits
|
|
141
|
+
"fake_bookstore_token", "xoxb-fake-bookstore-token", "fakebookstore", # lowercase words only
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@pytest.mark.parametrize("key", ["services.ledger.token", "services.ledger.key", "LEDGER_TOKEN", "MAILGUN_KEY"])
|
|
146
|
+
@pytest.mark.parametrize("value", _HOSTILE_ROWS)
|
|
147
|
+
def test_bare_key_and_token_redact_any_value_shape(key, value):
|
|
148
|
+
assert R.secret_key(key, value)
|
|
149
|
+
assert R.redact(value, key, "s1").startswith(R.MARK)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@pytest.mark.parametrize("key", ["services.ledger.token", "services.ledger.key", "LEDGER_TOKEN", "STRIPE_KEY"])
|
|
153
|
+
def test_bare_key_and_token_are_redacted_whatever_the_value(key):
|
|
154
|
+
assert R.secret_key(key) and R.secret_key(key, None) and R.secret_key(key, 12345) # no value: redact
|
|
155
|
+
assert R.secret_key(key, "sk_test_FAKE_bookstore_000000")
|
|
156
|
+
assert R.secret_key(key, "sk_fakebookstore") and R.secret_key(key, "pk_live_fakebookstore")
|
|
157
|
+
assert R.secret_key(key, "rk-fakebookstore")
|
|
158
|
+
assert R.secret_key(key, "base64:FAKEBOOKSTOREAPPKEY0000000000000000000000=")
|
|
159
|
+
assert R.secret_key(key, "Remember_Token") and R.secret_key(key, "token1")
|
|
160
|
+
assert R.secret_key(key, "a" * 41) and R.secret_key(key, R.marker("x", "s1"))
|
|
161
|
+
assert R.secret_key(key, "a" * 40)
|
|
162
|
+
assert R.secret_key(key, "created_at") and R.secret_key(key, "x-idempotency-key")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@pytest.mark.parametrize("key,value", [("primary_key", "id"), ("foreign_key", "author_id"), ("sort_key", "created_at"),
|
|
166
|
+
("remember_token", "remember_token"), ("cache.key", "bookstore_cache"),
|
|
167
|
+
("idempotency_key", "x-idempotency-key"), ("AUTH_GUARD", "web"),
|
|
168
|
+
("AUTH_PASSWORD_BROKER", "readers"),
|
|
169
|
+
("AUTH_PASSWORD_RESET_TOKEN_TABLE", "reader_password_resets"),
|
|
170
|
+
("try_it_credentials_policy", "include")])
|
|
171
|
+
def test_kept_cases(key, value):
|
|
172
|
+
assert not R.secret_key(key, value)
|
|
173
|
+
assert R.redact(value, key, "s1") == value
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@pytest.mark.parametrize("word", ["primary", "foreign", "sort", "partition", "route", "cache", "idempotency",
|
|
177
|
+
"remember", "csrf", "xsrf", "public", "i18n", "translation", "lookup", "unique",
|
|
178
|
+
"index", "storage", "prefix"])
|
|
179
|
+
@pytest.mark.parametrize("last", ["key", "token"])
|
|
180
|
+
def test_known_non_secret_compounds_keep_any_value(word, last):
|
|
181
|
+
assert not R.secret_key(f"{word}_{last}", "Fake0a1b" * 4)
|
|
182
|
+
assert not R.secret_key(f"{word.upper()}_{last.upper()}", "Fake0a1b" * 4)
|
|
183
|
+
assert not R.secret_key(f"app.{word}.{last}", "Fake0a1b" * 4)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@pytest.mark.parametrize("key", ["S3_KEY_ID", "S3_PUBLIC_KEY", "SIGNING_KEY_PATH", "SIGNING_KEY_FILE"])
|
|
187
|
+
def test_key_id_public_key_and_path_are_never_credentials(key):
|
|
188
|
+
assert not R.secret_key(key, "Fake0a1b" * 4)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def test_redact_keeps_a_guard_name_and_hides_a_password():
|
|
192
|
+
assert R.redact("web", "guard", "s1") == "web"
|
|
193
|
+
assert R.redact("readers", "AUTH_PASSWORD_BROKER", "s1") == "readers"
|
|
194
|
+
assert R.redact("fake-bookstore-pw-0000", "DB_PASSWORD", "s1").startswith(R.MARK)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@needs_php
|
|
198
|
+
def test_laravel_bookstore_config_keeps_defaults_and_redacts_secrets(tmp_path):
|
|
199
|
+
db = tmp_path / "laravel.db"
|
|
200
|
+
index_project(LARAVEL_FIX, db, "laravel-bookstore")
|
|
201
|
+
con = sqlite3.connect(db)
|
|
202
|
+
try:
|
|
203
|
+
attrs = {r[0]: json.loads(r[1] or "{}") for r in con.execute("SELECT id, attrs FROM nodes WHERE kind='config'")}
|
|
204
|
+
finally:
|
|
205
|
+
con.close()
|
|
206
|
+
assert attrs["config:auth.defaults.guard"]["env_default"] == ["web"]
|
|
207
|
+
assert attrs["config:auth.defaults.passwords"]["env_default"] == ["readers"]
|
|
208
|
+
assert attrs["config:auth.passwords.readers.table"]["env_default"] == ["reader_password_resets"]
|
|
209
|
+
assert attrs["config:shelfdocs.try_it_credentials_policy"]["value"] == "include"
|
|
210
|
+
assert attrs["config:models.primary_key"]["value"] == "id"
|
|
211
|
+
assert attrs["config:models.foreign_key"]["value"] == "author_id"
|
|
212
|
+
assert attrs["config:models.sort_key"]["value"] == "created_at"
|
|
213
|
+
assert attrs["config:models.remember_token"]["value"] == "remember_token"
|
|
214
|
+
assert attrs["config:models.cache.key"]["value"] == "bookstore_cache"
|
|
215
|
+
assert attrs["config:services.ledger.idempotency_key"]["value"] == "x-idempotency-key"
|
|
216
|
+
redacted = ["config:database.connections.pgsql.password", "config:services.ledger.token", "config:app.key",
|
|
217
|
+
"config:services.stripe.secret"]
|
|
218
|
+
for nid in redacted:
|
|
219
|
+
assert attrs[nid]["env_default"][0].startswith(R.MARK), nid
|
|
220
|
+
assert attrs["config:services.ledger.key"]["value"].startswith(R.MARK)
|
|
221
|
+
assert attrs["config:shelfdocs.client_secret"]["value"].startswith(R.MARK)
|
|
222
|
+
assert "fake-bookstore" not in json.dumps(attrs) and "FAKE" not in json.dumps(attrs)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@needs_php
|
|
226
|
+
def test_surface_php_fixture_still_redacts_exactly_three_values(tmp_path):
|
|
227
|
+
db = tmp_path / "php.db"
|
|
228
|
+
index_project(PHP_FIX, db, "php")
|
|
229
|
+
con = sqlite3.connect(db)
|
|
230
|
+
try:
|
|
231
|
+
attrs = [json.loads(r[0] or "{}") for r in con.execute("SELECT attrs FROM nodes WHERE kind='config'")]
|
|
232
|
+
finally:
|
|
233
|
+
con.close()
|
|
234
|
+
n = sum(1 for a in attrs for v in [a.get("value"), *(a.get("env_default") or [])] if R.is_redacted(v))
|
|
235
|
+
assert n == 3
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"""Salted secret fingerprints (#47 part 2d): `redacted:hmac:<8 hex>` is an HMAC-SHA-256 keyed with a per-graph salt kept
|
|
2
|
-
in the `meta` table (`redact_salt`), so a common password is not recognisable from its marker and markers only compare
|
|
3
|
-
within one graph (and across re-indexes of it)."""
|
|
4
|
-
import hashlib
|
|
5
|
-
import json
|
|
6
|
-
import shutil
|
|
7
|
-
import sqlite3
|
|
8
|
-
import sys
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
import pytest
|
|
12
|
-
|
|
13
|
-
ROOT = Path(__file__).resolve().parents[1]
|
|
14
|
-
sys.path.insert(0, str(ROOT))
|
|
15
|
-
from cg_code_graph.core import redact as R # noqa: E402
|
|
16
|
-
from cg_code_graph.indexer import index_project # noqa: E402
|
|
17
|
-
|
|
18
|
-
PHP_FIX = ROOT / "tests" / "surface_fixture" / "php"
|
|
19
|
-
needs_php = pytest.mark.skipif(shutil.which("php") is None, reason="php is not installed (PHP extractor)")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_marker_is_keyed_not_a_plain_digest():
|
|
23
|
-
plain = hashlib.sha256(b"password").hexdigest()[:8]
|
|
24
|
-
assert plain == "5e884898"
|
|
25
|
-
m = R.marker("password", "salt-a-0000")
|
|
26
|
-
assert m.startswith(R.MARK) and m != R.MARK + plain and m != R.LEGACY_MARK + plain
|
|
27
|
-
assert plain not in m
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def test_two_graphs_give_different_markers_and_one_graph_a_stable_one():
|
|
31
|
-
assert R.marker("hunter2", "salt-a") != R.marker("hunter2", "salt-b")
|
|
32
|
-
assert R.marker("hunter2", "salt-a") == R.marker("hunter2", "salt-a")
|
|
33
|
-
assert R.marker("hunter2", "salt-a") != R.marker("hunter3", "salt-a")
|
|
34
|
-
with pytest.raises(ValueError):
|
|
35
|
-
R.marker("hunter2", "")
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_redact_and_urls_use_the_salt_and_both_prefixes_count_as_redacted():
|
|
39
|
-
assert R.redact("fake-bookstore-pw-0000", "db_password", "s1") == R.marker("fake-bookstore-pw-0000", "s1")
|
|
40
|
-
url = R.redact("mysql://reader:fake-bookstore-pw-0000@replica.example.test/shop", None, "s1")
|
|
41
|
-
assert url == f"mysql://reader:{R.marker('fake-bookstore-pw-0000', 's1')}@replica.example.test/shop"
|
|
42
|
-
assert R.is_redacted(R.marker("x", "s1")) and R.is_redacted("redacted:sha256:5e884898")
|
|
43
|
-
assert R.redact(R.marker("x", "s1"), "password", "s2") == R.marker("x", "s1") # a marker is never re-hashed
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _config(db):
|
|
47
|
-
con = sqlite3.connect(db)
|
|
48
|
-
try:
|
|
49
|
-
salt = json.loads(con.execute("SELECT value FROM meta WHERE key='redact_salt'").fetchone()[0])
|
|
50
|
-
attrs = {r[0]: json.loads(r[1] or "{}") for r in con.execute("SELECT id, attrs FROM nodes WHERE kind='config'")}
|
|
51
|
-
finally:
|
|
52
|
-
con.close()
|
|
53
|
-
return salt, attrs["config:services.github.secret"]["value"]
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@needs_php
|
|
57
|
-
def test_salt_is_created_per_graph_and_kept_on_reindex(tmp_path):
|
|
58
|
-
a, b = tmp_path / "a.db", tmp_path / "b.db"
|
|
59
|
-
index_project(PHP_FIX, a, "php")
|
|
60
|
-
index_project(PHP_FIX, b, "php")
|
|
61
|
-
salt_a, marker_a = _config(a)
|
|
62
|
-
salt_b, marker_b = _config(b)
|
|
63
|
-
assert len(salt_a) == 32 and salt_a != salt_b
|
|
64
|
-
assert marker_a.startswith("redacted:hmac:") and marker_a != marker_b # two graphs, same value
|
|
65
|
-
index_project(PHP_FIX, a, "php") # re-index the same DB
|
|
66
|
-
assert _config(a) == (salt_a, marker_a)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@needs_php
|
|
70
|
-
def test_refresh_keeps_the_salt(tmp_path):
|
|
71
|
-
from cg_code_graph import hooks
|
|
72
|
-
root = tmp_path / "php"
|
|
73
|
-
shutil.copytree(PHP_FIX, root, ignore=shutil.ignore_patterns("vendor"))
|
|
74
|
-
db = tmp_path / "graph.db"
|
|
75
|
-
index_project(root, db, "php")
|
|
76
|
-
before = _config(db)
|
|
77
|
-
(root / "app" / "Services" / "Extra.php").write_text("<?php\nnamespace App\\Services;\nclass Extra {}\n")
|
|
78
|
-
assert hooks.refresh(str(root), str(db), "php", quiet=True) == 0
|
|
79
|
-
assert _config(db) == before
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@needs_php
|
|
83
|
-
def test_cg_link_keeps_markers_and_has_no_salt_of_its_own(tmp_path):
|
|
84
|
-
from cg_code_graph.link import link_many
|
|
85
|
-
a, b, out = tmp_path / "a.db", tmp_path / "b.db", tmp_path / "combined.db"
|
|
86
|
-
index_project(PHP_FIX, a, "php-a")
|
|
87
|
-
index_project(PHP_FIX, b, "php-b")
|
|
88
|
-
link_many([("one", str(a), "both"), ("two", str(b), "both")], str(out))
|
|
89
|
-
con = sqlite3.connect(out)
|
|
90
|
-
try:
|
|
91
|
-
assert con.execute("SELECT 1 FROM meta WHERE key='redact_salt'").fetchone() is None
|
|
92
|
-
vals = {r[0]: json.loads(r[1] or "{}").get("value") for r in con.execute(
|
|
93
|
-
"SELECT id, attrs FROM nodes WHERE id LIKE '%config:services.github.secret'")}
|
|
94
|
-
finally:
|
|
95
|
-
con.close()
|
|
96
|
-
assert len(vals) == 2 and all(str(v).startswith("redacted:hmac:") for v in vals.values())
|
|
97
|
-
assert len(set(vals.values())) == 2 # fingerprints of different source graphs do not compare
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|