classifyre-cli 0.4.74__tar.gz → 0.4.76__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.
- classifyre_cli-0.4.76/.turbo/turbo-build.log +3 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/PKG-INFO +1 -1
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/package.json +1 -1
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/pyproject.toml +1 -1
- classifyre_cli-0.4.76/src/detectors/engine_version.py +53 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/main.py +108 -7
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/models/generated_input.py +63 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/models/generated_single_asset_scan_results.py +52 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/outputs/rest.py +35 -3
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/pipeline/detector_pipeline.py +39 -4
- classifyre_cli-0.4.76/src/pipeline/scan_cache.py +545 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/atlassian_common.py +3 -2
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/base.py +24 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/email/source.py +8 -2
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/google_workspace/source.py +9 -2
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/local_folder/source.py +6 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/microsoft_365/source.py +7 -2
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/notion/client.py +3 -1
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/object_storage/base.py +34 -6
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/recipe_normalizer.py +11 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/file_parser.py +52 -10
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_s3_compatible_storage_source.py +58 -0
- classifyre_cli-0.4.76/tests/test_scan_cache.py +600 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_source_strategy_conformance.py +62 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_file_parser.py +73 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/uv.lock +28 -25
- classifyre_cli-0.4.74/.turbo/turbo-build.log +0 -3
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/.gitignore +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/.python-version +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/README.md +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/main.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/config.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/base.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/config.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/file_evaluation/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/file_evaluation/runner.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/models/generated_detectors.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/outputs/base.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/outputs/console.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/outputs/file.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/pipeline/parsed_content_provider.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/pipeline/text_artifact.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/asset_metadata.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/dropbox/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/dropbox/auth.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/dropbox/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/kafka/rest.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/sandbox/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/sandbox/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/sources/youtube/source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/telemetry.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/archive_extraction.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/dependency_groups.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/embedded_images.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/legacy_office.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/resources.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/transcription.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/validation.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/utils/video_processing.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/conftest.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/test_gliner2_classification.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/test_llm_runner.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/test_runner_hardening.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/pii/test_pii_chunking.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/pii/test_pii_severity.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/pipeline/test_detector_outcomes.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/pipeline/test_detector_pipeline.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_config.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_dependency_groups.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_dropbox_auth_command.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_dropbox_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_file_evaluation_runner.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_main.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_outputs.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_sandbox_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_youtube_source.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_archive_extraction.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_embedded_images.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_file_to_images.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_legacy_office.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_transcription.py +0 -0
- {classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/tests/utils/test_video_processing.py +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Version stamps that invalidate the scan cache when *our* behaviour changes.
|
|
2
|
+
|
|
3
|
+
The scan cache skips an asset when its content and each detector's configuration
|
|
4
|
+
fingerprint still match the previous successful run. Neither of those changes
|
|
5
|
+
when the change is ours — a new pattern in the PII detector, a new file format in
|
|
6
|
+
``utils/file_parser.py`` — so the fingerprint carries an explicit version that a
|
|
7
|
+
developer bumps by hand.
|
|
8
|
+
|
|
9
|
+
- Bump ``DETECTOR_ENGINE_VERSION[<type>]`` when a detector's rules, models,
|
|
10
|
+
thresholds or finding shape change. Every asset is then re-scanned by that
|
|
11
|
+
detector alone; the others stay cached.
|
|
12
|
+
- Bump ``PARSER_ENGINE_VERSION`` when text extraction changes what it produces —
|
|
13
|
+
a newly supported format, a different OCR path, a changed truncation rule.
|
|
14
|
+
That changes the *input* to every detector, so it invalidates everything.
|
|
15
|
+
|
|
16
|
+
Forgetting a bump is not a correctness bug, but it is an invisible one: the
|
|
17
|
+
improvement ships and silently does not apply to already-scanned assets. It
|
|
18
|
+
belongs on the release checklist.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import Final
|
|
24
|
+
|
|
25
|
+
PARSER_ENGINE_VERSION: Final[int] = 1
|
|
26
|
+
|
|
27
|
+
DETECTOR_ENGINE_VERSION: Final[dict[str, int]] = {
|
|
28
|
+
"SECRETS": 1,
|
|
29
|
+
"PII": 1,
|
|
30
|
+
"YARA": 1,
|
|
31
|
+
"BROKEN_LINKS": 1,
|
|
32
|
+
"CODE_SECURITY": 1,
|
|
33
|
+
"CUSTOM": 1,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Detectors whose verdict depends on state outside the asset, so identical
|
|
37
|
+
# content plus identical config does not imply an identical result.
|
|
38
|
+
#
|
|
39
|
+
# BROKEN_LINKS resolves URLs over the network: a link that answered 200 last week
|
|
40
|
+
# can 404 today with the document untouched. Caching it would freeze link health
|
|
41
|
+
# at whatever it was on first scan, which is the opposite of what the detector is
|
|
42
|
+
# for. These always run, even on an otherwise fully cached asset.
|
|
43
|
+
NON_CACHEABLE_DETECTOR_TYPES: Final[frozenset[str]] = frozenset({"BROKEN_LINKS"})
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def detector_engine_version(detector_type: str) -> int:
|
|
47
|
+
"""Engine version for a detector type; 0 for types added without a stamp."""
|
|
48
|
+
return DETECTOR_ENGINE_VERSION.get(detector_type.strip().upper(), 0)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def is_cacheable_detector_type(detector_type: str) -> bool:
|
|
52
|
+
"""False when the detector's result can change without the asset changing."""
|
|
53
|
+
return detector_type.strip().upper() not in NON_CACHEABLE_DETECTOR_TYPES
|
|
@@ -265,6 +265,16 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
265
265
|
worker_pool=worker_pool,
|
|
266
266
|
)
|
|
267
267
|
|
|
268
|
+
from .pipeline.scan_cache import ScanCache
|
|
269
|
+
|
|
270
|
+
scan_cache = ScanCache(recipe, source)
|
|
271
|
+
if scan_cache.enabled:
|
|
272
|
+
logger.info(
|
|
273
|
+
"Scan cache active (verify=%s): unchanged assets and"
|
|
274
|
+
" unchanged detectors will be skipped",
|
|
275
|
+
scan_cache.verify,
|
|
276
|
+
)
|
|
277
|
+
|
|
268
278
|
# --- Phase 1: Discovery ---
|
|
269
279
|
source.set_discovery_only(True)
|
|
270
280
|
all_stubs: list[Any] = []
|
|
@@ -286,9 +296,17 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
286
296
|
# change_type onto runner_assets rows during ingest, so
|
|
287
297
|
# the row must exist or the CREATED stamp is lost and
|
|
288
298
|
# first-run counters report every asset as unchanged.
|
|
299
|
+
#
|
|
300
|
+
# This is also the only point where the stored checksum
|
|
301
|
+
# still describes the *previous* scan — the ingest below
|
|
302
|
+
# overwrites it — so the scan cache reads prior state here
|
|
303
|
+
# or not at all.
|
|
289
304
|
hashes = [s["hash"] for s in stub_batch if s.get("hash")]
|
|
290
305
|
if hasattr(sink, "register_discovered_assets") and hashes:
|
|
291
|
-
await sink.register_discovered_assets(
|
|
306
|
+
prior = await sink.register_discovered_assets(
|
|
307
|
+
hashes, include_scan_cache=scan_cache.enabled
|
|
308
|
+
)
|
|
309
|
+
scan_cache.record_prior(prior)
|
|
292
310
|
|
|
293
311
|
await sink.emit_batch(stub_batch, skip_findings=True)
|
|
294
312
|
output_batch_count += 1
|
|
@@ -317,6 +335,8 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
317
335
|
max_concurrent,
|
|
318
336
|
)
|
|
319
337
|
error_count = 0
|
|
338
|
+
skipped_assets = 0
|
|
339
|
+
skipped_detector_runs = 0
|
|
320
340
|
chunk_errors: list[str] = []
|
|
321
341
|
|
|
322
342
|
async def _process_one(asset: Any) -> None:
|
|
@@ -325,8 +345,45 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
325
345
|
|
|
326
346
|
async def _process_one_inner(asset: Any) -> None:
|
|
327
347
|
nonlocal processed_count, error_count
|
|
348
|
+
nonlocal skipped_assets, skipped_detector_runs
|
|
328
349
|
asset_hash = getattr(asset, "hash", None) or ""
|
|
329
350
|
try:
|
|
351
|
+
plan = await scan_cache.plan(asset)
|
|
352
|
+
skipped_detector_runs += scan_cache.skipped_detector_runs(plan)
|
|
353
|
+
|
|
354
|
+
if plan.mode == "skip":
|
|
355
|
+
# Report no detector outcomes at all. The API
|
|
356
|
+
# may only resolve a finding whose detector
|
|
357
|
+
# reported OK on this asset, so an empty
|
|
358
|
+
# outcome set is what preserves the findings
|
|
359
|
+
# this run deliberately did not re-check.
|
|
360
|
+
skipped_assets += 1
|
|
361
|
+
processed_count += 1
|
|
362
|
+
logger.info("Cached %s: %s", asset.name, plan.reason)
|
|
363
|
+
prior = plan.prior
|
|
364
|
+
if hasattr(sink, "update_asset_status"):
|
|
365
|
+
await sink.update_asset_status(
|
|
366
|
+
asset_hash,
|
|
367
|
+
"PROCESSED",
|
|
368
|
+
findings_total=prior.findings_total if prior else None,
|
|
369
|
+
findings_by_severity=(
|
|
370
|
+
prior.findings_by_severity if prior else None
|
|
371
|
+
),
|
|
372
|
+
findings_by_detector=(
|
|
373
|
+
prior.findings_by_detector if prior else None
|
|
374
|
+
),
|
|
375
|
+
cache_hit=True,
|
|
376
|
+
)
|
|
377
|
+
source.evict_asset_cache(asset_hash)
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
if plan.mode == "partial":
|
|
381
|
+
logger.info(
|
|
382
|
+
"Partially cached %s: running %s",
|
|
383
|
+
asset.name,
|
|
384
|
+
", ".join(sorted(plan.run_detector_keys)),
|
|
385
|
+
)
|
|
386
|
+
|
|
330
387
|
if hasattr(sink, "update_asset_status"):
|
|
331
388
|
await sink.update_asset_status(asset_hash, "PROCESSING")
|
|
332
389
|
|
|
@@ -355,9 +412,17 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
355
412
|
asset,
|
|
356
413
|
on_findings_flushed=_on_findings_flushed,
|
|
357
414
|
findings_flush_size=args.detector_flush_batch_size,
|
|
415
|
+
only=(
|
|
416
|
+
plan.run_detector_keys if plan.mode == "partial" else None
|
|
417
|
+
),
|
|
358
418
|
)
|
|
359
|
-
|
|
360
|
-
|
|
419
|
+
|
|
420
|
+
# Chunks first, then the payload that carries the
|
|
421
|
+
# scan-cache state. A later run reads that state's
|
|
422
|
+
# presence as "this asset finished", so it must not
|
|
423
|
+
# be written while the asset's embeddings are still
|
|
424
|
+
# missing — that would strand them permanently.
|
|
425
|
+
chunks_ok = True
|
|
361
426
|
if hasattr(sink, "emit_text_chunks"):
|
|
362
427
|
artifact = pipeline.take_text_artifact(asset_hash)
|
|
363
428
|
if artifact is not None:
|
|
@@ -371,6 +436,7 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
371
436
|
# The run fails after phase 2 so operators
|
|
372
437
|
# cannot mistake missing embeddings for a
|
|
373
438
|
# healthy completed scan.
|
|
439
|
+
chunks_ok = False
|
|
374
440
|
detail = f"{asset_hash}: {chunk_exc}"
|
|
375
441
|
chunk_errors.append(detail)
|
|
376
442
|
logger.error(
|
|
@@ -380,10 +446,40 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
380
446
|
chunk_exc,
|
|
381
447
|
)
|
|
382
448
|
|
|
449
|
+
f_total, f_by_sev, f_by_det = _compute_findings_counts(
|
|
450
|
+
result.findings or []
|
|
451
|
+
)
|
|
452
|
+
payload = _asset_to_payload(result)
|
|
453
|
+
|
|
454
|
+
content_hash = plan.content_hash
|
|
455
|
+
if scan_cache.tracks_content_hash and content_hash is None:
|
|
456
|
+
# Bytes are still cached from the detector run,
|
|
457
|
+
# so this digest costs no extra fetch.
|
|
458
|
+
content_hash = await scan_cache.content_digest(asset)
|
|
459
|
+
|
|
460
|
+
state = scan_cache.build_state(
|
|
461
|
+
plan,
|
|
462
|
+
content_hash=content_hash,
|
|
463
|
+
detector_outcomes=(
|
|
464
|
+
result.scan_stats.detector_outcomes
|
|
465
|
+
if result.scan_stats
|
|
466
|
+
else None
|
|
467
|
+
),
|
|
468
|
+
scan_stats=result.scan_stats,
|
|
469
|
+
available_detector_keys=(
|
|
470
|
+
pipeline.available_detector_cache_keys()
|
|
471
|
+
),
|
|
472
|
+
completed=chunks_ok,
|
|
473
|
+
findings_total=f_total,
|
|
474
|
+
findings_by_severity=f_by_sev,
|
|
475
|
+
findings_by_detector=f_by_det,
|
|
476
|
+
)
|
|
477
|
+
if state is not None:
|
|
478
|
+
payload["scan_cache"] = state
|
|
479
|
+
|
|
480
|
+
await sink.emit_batch([payload], skip_findings=False)
|
|
481
|
+
|
|
383
482
|
if hasattr(sink, "update_asset_status"):
|
|
384
|
-
f_total, f_by_sev, f_by_det = _compute_findings_counts(
|
|
385
|
-
result.findings or []
|
|
386
|
-
)
|
|
387
483
|
await sink.update_asset_status(
|
|
388
484
|
asset_hash,
|
|
389
485
|
"PROCESSED",
|
|
@@ -409,10 +505,15 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
409
505
|
tasks = [asyncio.create_task(_process_one(a)) for a in all_stubs]
|
|
410
506
|
await asyncio.gather(*tasks, return_exceptions=True)
|
|
411
507
|
logger.info(
|
|
412
|
-
"Phase 2 complete: %d processed, %d errors"
|
|
508
|
+
"Phase 2 complete: %d processed (%d cached), %d errors,"
|
|
509
|
+
" %d detector run(s) skipped",
|
|
413
510
|
processed_count,
|
|
511
|
+
skipped_assets,
|
|
414
512
|
error_count,
|
|
513
|
+
skipped_detector_runs,
|
|
415
514
|
)
|
|
515
|
+
if hasattr(sink, "set_scan_cache_savings"):
|
|
516
|
+
sink.set_scan_cache_savings(skipped_assets, skipped_detector_runs)
|
|
416
517
|
if chunk_errors:
|
|
417
518
|
preview = "; ".join(chunk_errors[:3])
|
|
418
519
|
remaining = len(chunk_errors) - 3
|
|
@@ -134,6 +134,34 @@ class SamplingConfig(BaseModel):
|
|
|
134
134
|
)
|
|
135
135
|
|
|
136
136
|
|
|
137
|
+
class Verify(StrEnum):
|
|
138
|
+
"""
|
|
139
|
+
How strongly to prove the payload is unchanged. metadata trusts the source-reported checksum (provider ETag/MD5) and skips the download entirely. content downloads the bytes and compares a SHA-256 digest, still skipping parsing, OCR, transcription and detection. auto (default) picks per source: content where the checksum is only mtime and size (local folders), metadata where the provider supplies a content-derived digest.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
auto = 'auto'
|
|
143
|
+
metadata = 'metadata'
|
|
144
|
+
content = 'content'
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ScanCacheConfig(BaseModel):
|
|
148
|
+
"""
|
|
149
|
+
Skips re-processing assets whose content and applicable detector configuration are unchanged since their last fully-successful scan. Supported by file-like sources only (local folders, object storage, cloud drives, email); ignored elsewhere, because a database row can change under the same key with no change signal to compare against. A detector whose configuration changed is always re-run — and only that detector.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
model_config = ConfigDict(
|
|
153
|
+
extra='forbid',
|
|
154
|
+
)
|
|
155
|
+
enabled: bool | None = Field(
|
|
156
|
+
True,
|
|
157
|
+
description='When enabled (default), an asset whose content and per-detector configuration fingerprints all match its last successful scan is skipped, and its existing findings are carried forward untouched. Disable to re-scan everything on every run.',
|
|
158
|
+
)
|
|
159
|
+
verify: Verify | None = Field(
|
|
160
|
+
'auto',
|
|
161
|
+
description='How strongly to prove the payload is unchanged. metadata trusts the source-reported checksum (provider ETag/MD5) and skips the download entirely. content downloads the bytes and compares a SHA-256 digest, still skipping parsing, OCR, transcription and detection. auto (default) picks per source: content where the checksum is only mtime and size (local folders), metadata where the provider supplies a content-derived digest.',
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
137
165
|
class Requests(BaseModel):
|
|
138
166
|
model_config = ConfigDict(
|
|
139
167
|
extra='forbid',
|
|
@@ -2110,6 +2138,7 @@ class CoreInput(BaseModel):
|
|
|
2110
2138
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2111
2139
|
)
|
|
2112
2140
|
sampling: SamplingConfig
|
|
2141
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2113
2142
|
resources: ResourceOverrides | None = None
|
|
2114
2143
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2115
2144
|
True,
|
|
@@ -2132,6 +2161,7 @@ class EmailInput(CoreInput):
|
|
|
2132
2161
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2133
2162
|
)
|
|
2134
2163
|
sampling: SamplingConfig
|
|
2164
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2135
2165
|
resources: ResourceOverrides | None = None
|
|
2136
2166
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2137
2167
|
True,
|
|
@@ -2154,6 +2184,7 @@ class S3CompatibleStorageInput(CoreInput):
|
|
|
2154
2184
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2155
2185
|
)
|
|
2156
2186
|
sampling: SamplingConfig
|
|
2187
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2157
2188
|
resources: ResourceOverrides | None = None
|
|
2158
2189
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2159
2190
|
True,
|
|
@@ -2176,6 +2207,7 @@ class LocalFolderInput(CoreInput):
|
|
|
2176
2207
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2177
2208
|
)
|
|
2178
2209
|
sampling: SamplingConfig
|
|
2210
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2179
2211
|
resources: ResourceOverrides | None = None
|
|
2180
2212
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2181
2213
|
True,
|
|
@@ -2198,6 +2230,7 @@ class SandboxInput(CoreInput):
|
|
|
2198
2230
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2199
2231
|
)
|
|
2200
2232
|
sampling: SamplingConfig
|
|
2233
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2201
2234
|
resources: ResourceOverrides | None = None
|
|
2202
2235
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2203
2236
|
True,
|
|
@@ -2220,6 +2253,7 @@ class AzureBlobStorageInput(CoreInput):
|
|
|
2220
2253
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2221
2254
|
)
|
|
2222
2255
|
sampling: SamplingConfig
|
|
2256
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2223
2257
|
resources: ResourceOverrides | None = None
|
|
2224
2258
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2225
2259
|
True,
|
|
@@ -2242,6 +2276,7 @@ class GoogleCloudStorageInput(CoreInput):
|
|
|
2242
2276
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2243
2277
|
)
|
|
2244
2278
|
sampling: SamplingConfig
|
|
2279
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2245
2280
|
resources: ResourceOverrides | None = None
|
|
2246
2281
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2247
2282
|
True,
|
|
@@ -2264,6 +2299,7 @@ class WordPressInput(CoreInput):
|
|
|
2264
2299
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2265
2300
|
)
|
|
2266
2301
|
sampling: SamplingConfig
|
|
2302
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2267
2303
|
resources: ResourceOverrides | None = None
|
|
2268
2304
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2269
2305
|
True,
|
|
@@ -2286,6 +2322,7 @@ class PostgreSQLInput(CoreInput):
|
|
|
2286
2322
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2287
2323
|
)
|
|
2288
2324
|
sampling: SamplingConfig
|
|
2325
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2289
2326
|
resources: ResourceOverrides | None = None
|
|
2290
2327
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2291
2328
|
True,
|
|
@@ -2308,6 +2345,7 @@ class MySQLInput(CoreInput):
|
|
|
2308
2345
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2309
2346
|
)
|
|
2310
2347
|
sampling: SamplingConfig
|
|
2348
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2311
2349
|
resources: ResourceOverrides | None = None
|
|
2312
2350
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2313
2351
|
True,
|
|
@@ -2330,6 +2368,7 @@ class MSSQLInput(CoreInput):
|
|
|
2330
2368
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2331
2369
|
)
|
|
2332
2370
|
sampling: SamplingConfig
|
|
2371
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2333
2372
|
resources: ResourceOverrides | None = None
|
|
2334
2373
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2335
2374
|
True,
|
|
@@ -2352,6 +2391,7 @@ class OracleInput(CoreInput):
|
|
|
2352
2391
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2353
2392
|
)
|
|
2354
2393
|
sampling: SamplingConfig
|
|
2394
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2355
2395
|
resources: ResourceOverrides | None = None
|
|
2356
2396
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2357
2397
|
True,
|
|
@@ -2374,6 +2414,7 @@ class HiveInput(CoreInput):
|
|
|
2374
2414
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2375
2415
|
)
|
|
2376
2416
|
sampling: SamplingConfig
|
|
2417
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2377
2418
|
resources: ResourceOverrides | None = None
|
|
2378
2419
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2379
2420
|
True,
|
|
@@ -2400,6 +2441,7 @@ class DatabricksInput(CoreInput):
|
|
|
2400
2441
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2401
2442
|
)
|
|
2402
2443
|
sampling: SamplingConfig
|
|
2444
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2403
2445
|
resources: ResourceOverrides | None = None
|
|
2404
2446
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2405
2447
|
True,
|
|
@@ -2432,6 +2474,7 @@ class SnowflakeInput(CoreInput):
|
|
|
2432
2474
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2433
2475
|
)
|
|
2434
2476
|
sampling: SamplingConfig
|
|
2477
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2435
2478
|
resources: ResourceOverrides | None = None
|
|
2436
2479
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2437
2480
|
True,
|
|
@@ -2458,6 +2501,7 @@ class MongoDBInput(CoreInput):
|
|
|
2458
2501
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2459
2502
|
)
|
|
2460
2503
|
sampling: SamplingConfig
|
|
2504
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2461
2505
|
resources: ResourceOverrides | None = None
|
|
2462
2506
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2463
2507
|
True,
|
|
@@ -2588,6 +2632,7 @@ class Neo4jInput(CoreInput):
|
|
|
2588
2632
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2589
2633
|
)
|
|
2590
2634
|
sampling: SamplingConfig
|
|
2635
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2591
2636
|
resources: ResourceOverrides | None = None
|
|
2592
2637
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2593
2638
|
True,
|
|
@@ -2614,6 +2659,7 @@ class PowerBIInput(CoreInput):
|
|
|
2614
2659
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2615
2660
|
)
|
|
2616
2661
|
sampling: SamplingConfig
|
|
2662
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2617
2663
|
resources: ResourceOverrides | None = None
|
|
2618
2664
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2619
2665
|
True,
|
|
@@ -2640,6 +2686,7 @@ class TableauInput(CoreInput):
|
|
|
2640
2686
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
2641
2687
|
)
|
|
2642
2688
|
sampling: SamplingConfig
|
|
2689
|
+
scan_cache: ScanCacheConfig | None = None
|
|
2643
2690
|
resources: ResourceOverrides | None = None
|
|
2644
2691
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
2645
2692
|
True,
|
|
@@ -3051,6 +3098,7 @@ class ConfluenceInput(CoreInput):
|
|
|
3051
3098
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3052
3099
|
)
|
|
3053
3100
|
sampling: SamplingConfig
|
|
3101
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3054
3102
|
resources: ResourceOverrides | None = None
|
|
3055
3103
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3056
3104
|
True,
|
|
@@ -3073,6 +3121,7 @@ class JiraInput(CoreInput):
|
|
|
3073
3121
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3074
3122
|
)
|
|
3075
3123
|
sampling: SamplingConfig
|
|
3124
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3076
3125
|
resources: ResourceOverrides | None = None
|
|
3077
3126
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3078
3127
|
True,
|
|
@@ -3095,6 +3144,7 @@ class ServiceDeskInput(CoreInput):
|
|
|
3095
3144
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3096
3145
|
)
|
|
3097
3146
|
sampling: SamplingConfig
|
|
3147
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3098
3148
|
resources: ResourceOverrides | None = None
|
|
3099
3149
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3100
3150
|
True,
|
|
@@ -3155,6 +3205,7 @@ class SQLiteInput(CoreInput):
|
|
|
3155
3205
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3156
3206
|
)
|
|
3157
3207
|
sampling: SamplingConfig
|
|
3208
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3158
3209
|
resources: ResourceOverrides | None = None
|
|
3159
3210
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3160
3211
|
True,
|
|
@@ -3290,6 +3341,7 @@ class NotionInput(CoreInput):
|
|
|
3290
3341
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3291
3342
|
)
|
|
3292
3343
|
sampling: SamplingConfig
|
|
3344
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3293
3345
|
resources: ResourceOverrides | None = None
|
|
3294
3346
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3295
3347
|
True,
|
|
@@ -3537,6 +3589,7 @@ class KafkaInput(CoreInput):
|
|
|
3537
3589
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3538
3590
|
)
|
|
3539
3591
|
sampling: SamplingConfig
|
|
3592
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3540
3593
|
resources: ResourceOverrides | None = None
|
|
3541
3594
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3542
3595
|
True,
|
|
@@ -3671,6 +3724,7 @@ class ElasticsearchInput(CoreInput):
|
|
|
3671
3724
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3672
3725
|
)
|
|
3673
3726
|
sampling: SamplingConfig
|
|
3727
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3674
3728
|
resources: ResourceOverrides | None = None
|
|
3675
3729
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3676
3730
|
True,
|
|
@@ -3705,6 +3759,7 @@ class OpenSearchInput(CoreInput):
|
|
|
3705
3759
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3706
3760
|
)
|
|
3707
3761
|
sampling: SamplingConfig
|
|
3762
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3708
3763
|
resources: ResourceOverrides | None = None
|
|
3709
3764
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3710
3765
|
True,
|
|
@@ -3797,6 +3852,7 @@ class MeilisearchInput(CoreInput):
|
|
|
3797
3852
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
3798
3853
|
)
|
|
3799
3854
|
sampling: SamplingConfig
|
|
3855
|
+
scan_cache: ScanCacheConfig | None = None
|
|
3800
3856
|
resources: ResourceOverrides | None = None
|
|
3801
3857
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
3802
3858
|
True,
|
|
@@ -4037,6 +4093,7 @@ class Microsoft365Input(CoreInput):
|
|
|
4037
4093
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
4038
4094
|
)
|
|
4039
4095
|
sampling: SamplingConfig
|
|
4096
|
+
scan_cache: ScanCacheConfig | None = None
|
|
4040
4097
|
resources: ResourceOverrides | None = None
|
|
4041
4098
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
4042
4099
|
True,
|
|
@@ -4181,6 +4238,7 @@ class GoogleWorkspaceInput(CoreInput):
|
|
|
4181
4238
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
4182
4239
|
)
|
|
4183
4240
|
sampling: SamplingConfig
|
|
4241
|
+
scan_cache: ScanCacheConfig | None = None
|
|
4184
4242
|
resources: ResourceOverrides | None = None
|
|
4185
4243
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
4186
4244
|
True,
|
|
@@ -4393,6 +4451,7 @@ class DropboxInput(CoreInput):
|
|
|
4393
4451
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
4394
4452
|
)
|
|
4395
4453
|
sampling: SamplingConfig
|
|
4454
|
+
scan_cache: ScanCacheConfig | None = None
|
|
4396
4455
|
resources: ResourceOverrides | None = None
|
|
4397
4456
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
4398
4457
|
True,
|
|
@@ -4441,6 +4500,7 @@ class YouTubeInput(CoreInput):
|
|
|
4441
4500
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
4442
4501
|
)
|
|
4443
4502
|
sampling: SamplingConfig
|
|
4503
|
+
scan_cache: ScanCacheConfig | None = None
|
|
4444
4504
|
resources: ResourceOverrides | None = None
|
|
4445
4505
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
4446
4506
|
True,
|
|
@@ -4473,6 +4533,7 @@ class SlackInput(CoreInput):
|
|
|
4473
4533
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
4474
4534
|
)
|
|
4475
4535
|
sampling: SamplingConfig
|
|
4536
|
+
scan_cache: ScanCacheConfig | None = None
|
|
4476
4537
|
resources: ResourceOverrides | None = None
|
|
4477
4538
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
4478
4539
|
True,
|
|
@@ -4503,6 +4564,7 @@ class DeltaLakeInput(CoreInput):
|
|
|
4503
4564
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
4504
4565
|
)
|
|
4505
4566
|
sampling: SamplingConfig
|
|
4567
|
+
scan_cache: ScanCacheConfig | None = None
|
|
4506
4568
|
resources: ResourceOverrides | None = None
|
|
4507
4569
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
4508
4570
|
True,
|
|
@@ -4533,6 +4595,7 @@ class IcebergInput(CoreInput):
|
|
|
4533
4595
|
description='Reusable custom detector IDs selected from the custom detector catalog.',
|
|
4534
4596
|
)
|
|
4535
4597
|
sampling: SamplingConfig
|
|
4598
|
+
scan_cache: ScanCacheConfig | None = None
|
|
4536
4599
|
resources: ResourceOverrides | None = None
|
|
4537
4600
|
cleanup_removed_detector_findings: bool | None = Field(
|
|
4538
4601
|
True,
|
{classifyre_cli-0.4.74 → classifyre_cli-0.4.76}/src/models/generated_single_asset_scan_results.py
RENAMED
|
@@ -211,6 +211,53 @@ class DetectorOutcome(BaseModel):
|
|
|
211
211
|
)
|
|
212
212
|
|
|
213
213
|
|
|
214
|
+
class ScanCacheState(BaseModel):
|
|
215
|
+
"""
|
|
216
|
+
What a later run needs in order to prove this asset can be skipped, and what it should report when it does. Only a state with complete=true is eligible for reuse. A detector that raised or failed to initialize is omitted from the fingerprint map so it is retried next run rather than silently trusted.
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
complete: bool = Field(
|
|
220
|
+
...,
|
|
221
|
+
description='True only after findings, extraction and text chunks all completed. False is an explicit tombstone that invalidates older reusable state.',
|
|
222
|
+
title='Complete',
|
|
223
|
+
)
|
|
224
|
+
findings_total: int | None = Field(
|
|
225
|
+
None,
|
|
226
|
+
description='Findings this scan saw on the asset, carried forward so a later skipped run reports the same total instead of zero',
|
|
227
|
+
title='Findings Total',
|
|
228
|
+
)
|
|
229
|
+
findings_by_severity: dict[str, int] | None = Field(
|
|
230
|
+
None,
|
|
231
|
+
description='Finding counts per severity, carried forward to a skipped run',
|
|
232
|
+
title='Findings By Severity',
|
|
233
|
+
)
|
|
234
|
+
findings_by_detector: dict[str, dict[str, int]] | None = Field(
|
|
235
|
+
None,
|
|
236
|
+
description='Finding counts per detector and severity, carried forward to a skipped run',
|
|
237
|
+
title='Findings By Detector',
|
|
238
|
+
)
|
|
239
|
+
empty_text: bool | None = Field(
|
|
240
|
+
None,
|
|
241
|
+
description='Whether text extraction yielded nothing, carried forward so a skipped run does not distort text-coverage reporting',
|
|
242
|
+
title='Empty Text',
|
|
243
|
+
)
|
|
244
|
+
text_extraction_status: str | None = Field(
|
|
245
|
+
None,
|
|
246
|
+
description='Text extraction outcome, carried forward to a skipped run',
|
|
247
|
+
title='Text Extraction Status',
|
|
248
|
+
)
|
|
249
|
+
content_hash: str | None = Field(
|
|
250
|
+
None,
|
|
251
|
+
description='SHA-256 of the bytes that were actually scanned. Null when the source proved sameness from a provider-supplied digest without downloading.',
|
|
252
|
+
title='Content Hash',
|
|
253
|
+
)
|
|
254
|
+
detectors: dict[str, str] | None = Field(
|
|
255
|
+
{},
|
|
256
|
+
description='Fingerprint of the configuration each detector last succeeded with, keyed by detector type or CUSTOM::<custom_detector_key>. A key whose fingerprint still matches on a later run means that detector would produce identical findings and can be skipped.',
|
|
257
|
+
title='Detectors',
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
214
261
|
class ScanStats(BaseModel):
|
|
215
262
|
"""
|
|
216
263
|
Statistics about detector scan for an asset
|
|
@@ -307,3 +354,8 @@ class SingleAssetScanResults(BaseModel):
|
|
|
307
354
|
description='Source-specific asset metadata using normalized keys (size_bytes, row_count, etc.) where applicable',
|
|
308
355
|
title='Metadata',
|
|
309
356
|
)
|
|
357
|
+
scan_cache: ScanCacheState | None = Field(
|
|
358
|
+
None,
|
|
359
|
+
description='Scan-cache state to persist for this asset, letting a later run skip work that would produce identical results',
|
|
360
|
+
title='Scan Cache',
|
|
361
|
+
)
|