classifyre-cli 0.4.78__tar.gz → 0.4.80__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.80/.turbo/turbo-build.log +3 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/PKG-INFO +1 -1
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/package.json +1 -1
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/pyproject.toml +11 -1
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/file_evaluation/runner.py +39 -34
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/main.py +32 -2
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/models/generated_detectors.py +1 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/models/generated_input.py +368 -3
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/models/generated_single_asset_scan_results.py +52 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/outputs/rest.py +22 -5
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/pipeline/parsed_content_provider.py +1 -1
- classifyre_cli-0.4.80/src/pipeline/payload_window.py +554 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/pipeline/scan_cache.py +17 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/base.py +67 -8
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/object_storage/base.py +257 -95
- classifyre_cli-0.4.80/src/sources/reddit/__init__.py +3 -0
- classifyre_cli-0.4.80/src/sources/reddit/source.py +1199 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/youtube/source.py +46 -156
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/dependency_groups.py +1 -0
- classifyre_cli-0.4.80/src/utils/embedded_files.py +344 -0
- classifyre_cli-0.4.80/src/utils/external_video.py +336 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/file_parser.py +182 -22
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/pipeline/test_detector_pipeline.py +4 -2
- classifyre_cli-0.4.80/tests/test_object_storage_download_efficiency.py +267 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_outputs.py +75 -0
- classifyre_cli-0.4.80/tests/test_payload_window.py +517 -0
- classifyre_cli-0.4.80/tests/test_payload_window_parquet.py +296 -0
- classifyre_cli-0.4.80/tests/test_reddit_source.py +762 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_s3_compatible_storage_source.py +151 -10
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_scan_cache.py +122 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_source_strategy_conformance.py +39 -0
- classifyre_cli-0.4.80/tests/utils/test_embedded_files.py +246 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/uv.lock +100 -46
- classifyre_cli-0.4.78/.turbo/turbo-build.log +0 -3
- classifyre_cli-0.4.78/src/utils/embedded_images.py +0 -222
- classifyre_cli-0.4.78/tests/utils/test_embedded_images.py +0 -129
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/.gitignore +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/.python-version +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/README.md +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/main.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/scripts/generate_models.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/config.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/base.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/broken_links/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/broken_links/detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/config.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/extractor.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/_base.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/_factory.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/_gliner2.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/_image_classification.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/_llm.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/_object_detection.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/_regex.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/runners/_text_classification.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/custom/trainer.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/dependencies.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/engine_version.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/pii/detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/secrets/detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/threat/code_security_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/detectors/threat/yara_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/file_evaluation/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/outputs/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/outputs/base.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/outputs/console.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/outputs/factory.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/outputs/file.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/pipeline/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/pipeline/content_provider.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/pipeline/detector_pipeline.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/pipeline/text_artifact.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/pipeline/worker_pool.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/asset_metadata.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/atlassian_common.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/azure_blob_storage/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/azure_blob_storage/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/confluence/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/confluence/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/databricks/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/databricks/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/delta_lake/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/delta_lake/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/dependencies.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/dropbox/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/dropbox/auth.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/dropbox/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/elasticsearch/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/elasticsearch/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/email/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/email/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/google_cloud_storage/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/google_cloud_storage/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/google_workspace/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/google_workspace/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/hive/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/hive/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/hugging_face/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/hugging_face/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/iceberg/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/iceberg/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/jira/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/jira/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/kafka/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/kafka/rest.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/kafka/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/lakehouse_base.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/local_folder/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/local_folder/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/meilisearch/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/meilisearch/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/microsoft_365/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/microsoft_365/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/mongodb/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/mongodb/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/mssql/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/mssql/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/mysql/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/mysql/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/neo4j/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/neo4j/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/notion/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/notion/client.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/notion/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/opensearch/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/opensearch/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/oracle/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/oracle/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/postgresql/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/postgresql/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/powerbi/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/powerbi/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/s3_client.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/s3_compatible_storage/README.md +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/s3_compatible_storage/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/s3_compatible_storage/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/sandbox/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/sandbox/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/search_engine_base.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/servicedesk/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/servicedesk/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/slack/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/slack/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/snowflake/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/snowflake/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/sqlite/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/sqlite/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/tableau/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/tableau/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/tabular_base.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/tabular_utils.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/wordpress/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/wordpress/source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/sources/youtube/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/telemetry.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/archive_extraction.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/content_extraction.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/file_metadata.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/file_to_images.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/hashing.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/legacy_office.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/resources.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/transcription.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/uv_sync.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/validation.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/src/utils/video_processing.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/_lakehouse_fakes.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/conftest.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/broken_links/test_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/conftest.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/content/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/conftest.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/test_gliner2_classification.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/test_invoice_extraction.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/test_llm_runner.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/test_pipeline_integration.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/test_regex_runner.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/test_runner_hardening.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/custom/test_transformer_runners.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/pii/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/pii/conftest.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/pii/sample_invoice.pdf +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/pii/test_pii_chunking.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/pii/test_pii_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/pii/test_pii_detector_extended.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/pii/test_pii_regional_entities.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/pii/test_pii_severity.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/secrets/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/secrets/test_secrets_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/secrets/test_secrets_detector_extended.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/test_base_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/test_custom_detector_examples_runtime.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/test_detector_catalog_commercial.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/test_detector_pipeline_types.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/test_detector_schema_examples.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/test_detector_types.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/test_phase2_detectors.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/test_registry.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/threat/__init__.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/threat/test_code_security_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/detectors/threat/test_yara_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/integration/test_wordpress_broken_links_detector.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/integration/test_wordpress_links_assets.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/pipeline/test_detector_outcomes.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/pipeline/test_worker_pool.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_assets_metadata_catalog.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_azure_blob_storage_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_base_source_attachment.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_base_source_sampling.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_config.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_confluence_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_custom_extractor.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_databricks_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_delta_lake_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_dependency_groups.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_dropbox_auth_command.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_dropbox_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_elasticsearch_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_email_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_file_evaluation_runner.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_google_cloud_storage_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_google_workspace_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_hashing.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_hive_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_hugging_face_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_iceberg_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_jira_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_kafka_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_local_folder_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_main.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_meilisearch_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_microsoft_365_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_mongodb_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_mssql_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_mysql_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_neo4j_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_notion_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_opensearch_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_oracle_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_postgresql_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_powerbi_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_recipe_normalizer.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_sampling_automatic.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_sandbox_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_servicedesk_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_slack_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_snowflake_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_source_dependency_groups.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_sqlite_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_tableau_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_tabular_automatic_sampling.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_tabular_utils.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_uv_sync.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_wordpress_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_youtube_source.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/test_youtube_source_integration.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/utils/test_archive_extraction.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/utils/test_content_extraction.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/utils/test_file_metadata.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/utils/test_file_parser.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/utils/test_file_to_images.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/utils/test_legacy_office.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/utils/test_transcription.py +0 -0
- {classifyre_cli-0.4.78 → classifyre_cli-0.4.80}/tests/utils/test_video_processing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "classifyre-cli"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.80"
|
|
4
4
|
description = "Classifyre CLI — scan and classify unstructured data sources"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -246,6 +246,16 @@ youtube = [
|
|
|
246
246
|
"yt-dlp>=2025.1.0",
|
|
247
247
|
"youtube-transcript-api>=1.0.0",
|
|
248
248
|
]
|
|
249
|
+
reddit = [
|
|
250
|
+
# Official Reddit API wrapper. Handles OAuth (all three PRAW flows), token
|
|
251
|
+
# refresh and Reddit's own ratelimit headers.
|
|
252
|
+
"praw>=7.8.1,<8.0.0",
|
|
253
|
+
# Reddit posts routinely embed YouTube/Vimeo links; those are analysed with
|
|
254
|
+
# the same yt-dlp + captions + Whisper + frame-OCR pipeline the YouTube
|
|
255
|
+
# source uses (src/utils/external_video.py).
|
|
256
|
+
"yt-dlp>=2025.1.0",
|
|
257
|
+
"youtube-transcript-api>=1.0.0",
|
|
258
|
+
]
|
|
249
259
|
delta-lake = [
|
|
250
260
|
# Pure-Python/Rust Delta reader (delta-rs) — no Spark, no JVM. Table metadata
|
|
251
261
|
# and data-file lists come from deltalake; rows are sampled through DuckDB
|
|
@@ -9,7 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
11
|
from ..models.generated_single_asset_scan_results import DetectionResult, Location
|
|
12
|
-
from ..utils.
|
|
12
|
+
from ..utils.embedded_files import has_embedded_files, iter_embedded_files
|
|
13
13
|
from ..utils.file_parser import ParsedFile, parse_file
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
@@ -148,11 +148,11 @@ class FileEvaluationRunner:
|
|
|
148
148
|
file_mime,
|
|
149
149
|
)
|
|
150
150
|
|
|
151
|
-
# Files that embed
|
|
152
|
-
# embedded
|
|
153
|
-
# findings tagged by the embedded
|
|
154
|
-
|
|
155
|
-
if not tasks and not
|
|
151
|
+
# Files that embed whole other files (parquet file columns, office media)
|
|
152
|
+
# get each embedded file run through the binary detectors directly, with
|
|
153
|
+
# findings tagged by the embedded location so the UI can group them.
|
|
154
|
+
embedded_files = has_embedded_files(mime_type)
|
|
155
|
+
if not tasks and not embedded_files:
|
|
156
156
|
if not text.strip() and not file_mime:
|
|
157
157
|
logger.warning(
|
|
158
158
|
"No text content extracted from %s file; skipping text detectors.",
|
|
@@ -186,9 +186,9 @@ class FileEvaluationRunner:
|
|
|
186
186
|
)
|
|
187
187
|
)
|
|
188
188
|
|
|
189
|
-
if
|
|
189
|
+
if embedded_files:
|
|
190
190
|
all_findings.extend(
|
|
191
|
-
await self.
|
|
191
|
+
await self._run_embedded_file_detectors(
|
|
192
192
|
detectors=detectors,
|
|
193
193
|
file_bytes=_get_raw_bytes(),
|
|
194
194
|
mime_type=mime_type,
|
|
@@ -198,7 +198,7 @@ class FileEvaluationRunner:
|
|
|
198
198
|
|
|
199
199
|
return parsed, all_findings
|
|
200
200
|
|
|
201
|
-
async def
|
|
201
|
+
async def _run_embedded_file_detectors(
|
|
202
202
|
self,
|
|
203
203
|
*,
|
|
204
204
|
detectors: list[Any],
|
|
@@ -206,60 +206,65 @@ class FileEvaluationRunner:
|
|
|
206
206
|
mime_type: str,
|
|
207
207
|
detected_at: datetime,
|
|
208
208
|
) -> list[DetectionResult]:
|
|
209
|
-
"""Run
|
|
209
|
+
"""Run binary detectors over each file embedded in a container file.
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
Unwindowed on purpose: file evaluation judges one file the user handed over,
|
|
212
|
+
so there is no run-to-run cursor to advance. A scan of the same container
|
|
213
|
+
from a source expands its embedded files into child assets instead, one
|
|
214
|
+
sampling window at a time.
|
|
215
|
+
|
|
216
|
+
Sequential by design (one file, one detector at a time) so the job/scan
|
|
212
217
|
logs read in order and each detector's outcome is attributable.
|
|
213
218
|
"""
|
|
214
219
|
binary_detectors = [d for d in detectors if self._is_binary_detector(d)]
|
|
215
220
|
if not binary_detectors:
|
|
216
221
|
logger.info(
|
|
217
|
-
"File evaluation: %s embeds
|
|
218
|
-
"skipping embedded-
|
|
222
|
+
"File evaluation: %s embeds files but no binary-capable detectors are configured; "
|
|
223
|
+
"skipping embedded-file scan",
|
|
219
224
|
mime_type,
|
|
220
225
|
)
|
|
221
226
|
return []
|
|
222
227
|
|
|
223
228
|
logger.info(
|
|
224
|
-
"File evaluation: scanning
|
|
229
|
+
"File evaluation: scanning files embedded in %s with binary detectors: [%s]",
|
|
225
230
|
mime_type,
|
|
226
231
|
", ".join(self._detector_label(d) for d in binary_detectors),
|
|
227
232
|
)
|
|
228
233
|
|
|
229
234
|
findings: list[DetectionResult] = []
|
|
230
|
-
|
|
231
|
-
for
|
|
232
|
-
|
|
233
|
-
size_kb = len(
|
|
235
|
+
embedded_count = 0
|
|
236
|
+
for embedded in iter_embedded_files(file_bytes, mime_type):
|
|
237
|
+
embedded_count += 1
|
|
238
|
+
size_kb = len(embedded.file_bytes) / 1024
|
|
234
239
|
compatible = [
|
|
235
240
|
d
|
|
236
241
|
for d in binary_detectors
|
|
237
|
-
if self._supports_mime(d.get_supported_content_types(),
|
|
242
|
+
if self._supports_mime(d.get_supported_content_types(), embedded.mime_type)
|
|
238
243
|
]
|
|
239
244
|
if not compatible:
|
|
240
245
|
logger.info(
|
|
241
|
-
"File evaluation: embedded
|
|
242
|
-
|
|
243
|
-
|
|
246
|
+
"File evaluation: embedded file %s (%s, %.0f KB) — no compatible detector",
|
|
247
|
+
embedded.location,
|
|
248
|
+
embedded.mime_type,
|
|
244
249
|
size_kb,
|
|
245
250
|
)
|
|
246
251
|
continue
|
|
247
252
|
logger.info(
|
|
248
|
-
"File evaluation: embedded
|
|
249
|
-
|
|
250
|
-
|
|
253
|
+
"File evaluation: embedded file %s (%s, %.0f KB) → dispatching [%s]",
|
|
254
|
+
embedded.location,
|
|
255
|
+
embedded.mime_type,
|
|
251
256
|
size_kb,
|
|
252
257
|
", ".join(self._detector_label(d) for d in compatible),
|
|
253
258
|
)
|
|
254
259
|
for detector in compatible:
|
|
255
260
|
label = self._detector_label(detector)
|
|
256
261
|
try:
|
|
257
|
-
result = await detector.detect(
|
|
262
|
+
result = await detector.detect(embedded.file_bytes, embedded.mime_type)
|
|
258
263
|
except Exception as exc:
|
|
259
264
|
logger.error(
|
|
260
|
-
"File evaluation: '%s' failed on embedded
|
|
265
|
+
"File evaluation: '%s' failed on embedded file %s: %s",
|
|
261
266
|
label,
|
|
262
|
-
|
|
267
|
+
embedded.location,
|
|
263
268
|
exc,
|
|
264
269
|
)
|
|
265
270
|
continue
|
|
@@ -269,17 +274,17 @@ class FileEvaluationRunner:
|
|
|
269
274
|
else []
|
|
270
275
|
)
|
|
271
276
|
logger.info(
|
|
272
|
-
"File evaluation: '%s' on embedded
|
|
277
|
+
"File evaluation: '%s' on embedded file %s → %d finding(s)",
|
|
273
278
|
label,
|
|
274
|
-
|
|
279
|
+
embedded.location,
|
|
275
280
|
len(detector_findings),
|
|
276
281
|
)
|
|
277
282
|
for finding in detector_findings:
|
|
278
|
-
findings.append(self._tag_embedded(finding,
|
|
283
|
+
findings.append(self._tag_embedded(finding, embedded.location, detected_at))
|
|
279
284
|
|
|
280
285
|
logger.info(
|
|
281
|
-
"File evaluation: embedded-
|
|
282
|
-
|
|
286
|
+
"File evaluation: embedded-file scan complete — %d file(s) scanned, %d finding(s)",
|
|
287
|
+
embedded_count,
|
|
283
288
|
len(findings),
|
|
284
289
|
)
|
|
285
290
|
return findings
|
|
@@ -299,7 +304,7 @@ class FileEvaluationRunner:
|
|
|
299
304
|
def _tag_embedded(
|
|
300
305
|
finding: DetectionResult, location_label: str, detected_at: datetime
|
|
301
306
|
) -> DetectionResult:
|
|
302
|
-
"""Attach the embedded-
|
|
307
|
+
"""Attach the embedded-file location to a finding for parent-file grouping."""
|
|
303
308
|
metadata = dict(finding.metadata or {})
|
|
304
309
|
metadata["embedded_location"] = location_label
|
|
305
310
|
location = finding.location or Location()
|
|
@@ -265,9 +265,22 @@ 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.payload_window import PayloadWindowStore
|
|
268
269
|
from .pipeline.scan_cache import ScanCache
|
|
269
270
|
|
|
270
|
-
|
|
271
|
+
# Bounds how much of each asset a run reads, not just how many
|
|
272
|
+
# assets it reads. The scan cache consults it before skipping,
|
|
273
|
+
# so a part-scanned payload is never mistaken for a finished one.
|
|
274
|
+
payload_windows = PayloadWindowStore.from_recipe(recipe)
|
|
275
|
+
if payload_windows.enabled:
|
|
276
|
+
logger.info(
|
|
277
|
+
"Payload sampling active (%s): tabular assets are read %d rows per run",
|
|
278
|
+
payload_windows.strategy,
|
|
279
|
+
payload_windows.rows_per_page,
|
|
280
|
+
)
|
|
281
|
+
source.attach_payload_windows(payload_windows)
|
|
282
|
+
|
|
283
|
+
scan_cache = ScanCache(recipe, source, payload_windows=payload_windows)
|
|
271
284
|
if scan_cache.enabled:
|
|
272
285
|
logger.info(
|
|
273
286
|
"Scan cache active (verify=%s): unchanged assets and"
|
|
@@ -292,6 +305,13 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
292
305
|
for stub in stub_batch:
|
|
293
306
|
stub["findings"] = []
|
|
294
307
|
|
|
308
|
+
# Teach the payload windows each asset's ids and checksum
|
|
309
|
+
# before any window is built: a stored row offset only
|
|
310
|
+
# resumes when it still matches the file it was measured
|
|
311
|
+
# against.
|
|
312
|
+
for asset in raw_batch:
|
|
313
|
+
payload_windows.bind(asset)
|
|
314
|
+
|
|
295
315
|
# Register BEFORE ingesting: the API stamps per-run
|
|
296
316
|
# change_type onto runner_assets rows during ingest, so
|
|
297
317
|
# the row must exist or the CREATED stamp is lost and
|
|
@@ -303,9 +323,15 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
303
323
|
# or not at all.
|
|
304
324
|
hashes = [s["hash"] for s in stub_batch if s.get("hash")]
|
|
305
325
|
if hasattr(sink, "register_discovered_assets") and hashes:
|
|
326
|
+
want_cursors = payload_windows.persists_cursor
|
|
306
327
|
prior = await sink.register_discovered_assets(
|
|
307
|
-
hashes,
|
|
328
|
+
hashes,
|
|
329
|
+
include_scan_cache=scan_cache.enabled,
|
|
330
|
+
include_payload_cursor=want_cursors,
|
|
308
331
|
)
|
|
332
|
+
if want_cursors and isinstance(prior, tuple):
|
|
333
|
+
prior, payload_cursors = prior
|
|
334
|
+
payload_windows.record_prior(payload_cursors)
|
|
309
335
|
scan_cache.record_prior(prior)
|
|
310
336
|
|
|
311
337
|
await sink.emit_batch(stub_batch, skip_findings=True)
|
|
@@ -477,6 +503,10 @@ async def run_command_async(args: argparse.Namespace, recipe: dict[str, Any]) ->
|
|
|
477
503
|
if state is not None:
|
|
478
504
|
payload["scan_cache"] = state
|
|
479
505
|
|
|
506
|
+
cursor = payload_windows.cursor_payload_for(asset)
|
|
507
|
+
if cursor is not None:
|
|
508
|
+
payload["payload_cursor"] = cursor
|
|
509
|
+
|
|
480
510
|
await sink.emit_batch([payload], skip_findings=False)
|
|
481
511
|
|
|
482
512
|
if hasattr(sink, "update_asset_status"):
|