Skip to main content
Model scanning is part of HiddenLayer AI Supply Chain Security. A scan inspects model files for security risks, such as malicious code embedded in a serialized model, before you load or deploy them. The SDK uploads your model to HiddenLayer (or asks HiddenLayer to fetch it), waits for the scan to finish, and returns a scan report. Use model scanning when you:
  • Download a third-party model and want to check it before loading it.
  • Want to block risky models in a CI/CD pipeline.
  • Need a record of scan results for models stored in Hugging Face, AWS S3, or Azure Blob Storage.

Before you begin

  • Install the SDK and configure credentials. See Installation and Authentication and configuration.
  • Set HIDDENLAYER_CLIENT_ID and HIDDENLAYER_CLIENT_SECRET in your environment. Alternatively, set HIDDENLAYER_TOKEN to a bearer token. Every example on this page reads credentials from these variables. None of them hard-code secrets.
  • Your tenant must have AI Supply Chain Security enabled, and your API key needs permission to use it.
Some helpers need extra packages. These are not installed with the SDK, and they are not SDK extras. Install them separately only if you use the matching helper:

Understand the scan report

Every scan method returns a ScanReport. A ScanReport is a Pydantic model, which means you read its fields as attributes, such as report.status. Import the type with from hiddenlayer.types.scans import ScanReport if you want type hints. The fields you use most often are: Each item in file_results[].detections includes severity (critical, high, medium, or low), category, description, risk (MALICIOUS or SUSPICIOUS), rule_id, and mapping fields such as cve, cwe, mitre_atlas, and owasp.
ScanReport also has top-level detection_count, file_count, files_with_detections_count, severity, and detection_categories fields. These are deprecated. Read the same values from report.summary instead. Some older examples, including examples/demo.py in the SDK repository, still use the deprecated fields.
A scan that fails is returned, not raised as an exception. Always check report.status before you trust the summary. A failed scan may have zero detections only because the scan did not complete.

Choose a scan method

All methods accept wait_for_results (default True). When it is True, the method blocks until the scan finishes. See Start a scan without waiting for the alternative.

Scan a local file

Use scan_file to scan one model file, such as a .pkl, .pt, .h5, or .safetensors file. The SDK uploads the file in parts, tells HiddenLayer the upload is complete, and then polls until the scan finishes. The following script scans a file, prints the results, and exits with a non-zero code if the scan fails or finds a critical or high detection. You can run it as a CI gate: most CI systems fail the job when a step exits with a non-zero code.
scan_file.py
Expected result: the script prints the scan ID, status, highest severity, and detection count. For each file it prints any detections. For a model with no detections, the status is done, the highest severity is typically none, and the script exits with code 0. The model_name and model_version values identify the model in the HiddenLayer Console. model_version defaults to "1".

Scan a folder

Use scan_folder when a model consists of several files, such as a weights file plus configuration and tokenizer files. The SDK walks the folder recursively and uploads every file that passes the filters. You can filter files with fnmatch patterns (shell-style wildcards such as *.bin):
  • allow_file_patterns: if set, only files that match at least one pattern are scanned.
  • ignore_file_patterns: files that match any pattern are skipped.
The SDK always excludes these patterns, in addition to any you pass: *.txt, *.md, *.lock, .gitattributes, and .git directories.
scan_folder.py
Expected result: one report that covers every uploaded file, with one entry per file in report.file_results.
If HiddenLayer rejects a file as a duplicate within the same scan, the SDK logs a warning and skips that file instead of failing the whole scan.

Scan a Hugging Face model

Use scan_huggingface_model to scan a model repository on Hugging Face. The SDK downloads the repository with huggingface_hub, then scans the downloaded folder. Extra prerequisite: pip install huggingface_hub. For private or gated repositories, provide a Hugging Face access token with hf_token:
  • Pass a string to use that token. The example reads it from an HF_TOKEN environment variable that you set yourself.
  • Pass True to read the token saved in your local Hugging Face configuration (for example, after you log in with the Hugging Face CLI).
scan_huggingface.py
If you omit model_name, the SDK uses repo_id as the model name. If you omit revision, the model version is "1". Other options are local_dir (download location), ignore_file_patterns, and force_download. Expected result: a report for the downloaded repository with report.inventory.origin set to Hugging Face.

Scan a model in AWS S3

Use scan_s3_model to scan a single file stored in S3. The SDK downloads the file with boto3 and then calls scan_file. Extra prerequisite: pip install boto3. By default the SDK creates boto3.client("s3"), which uses the standard AWS credential chain. That chain checks sources such as the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables, your AWS config files, and attached IAM roles. Pass your own client with s3_client if you need a specific profile or region.
scan_s3.py
Expected result: a report with report.inventory.origin set to S3.

Scan a model in Azure Blob Storage

Use scan_azure_blob_model to scan a single blob. The SDK downloads the blob with the Azure Storage SDK and then calls scan_file. Extra prerequisite: pip install azure-identity azure-storage-blob. By default the SDK authenticates with DefaultAzureCredential, which tries several Azure sign-in methods, such as environment variables, managed identity, and the Azure CLI. To use a shared access signature (SAS) token instead, pass it as credential. Read it from an environment variable rather than writing it in your code.
scan_azure_default.py
Expected result: a report with report.inventory.origin set to Azure Blob Storage.

Temporary files for downloaded models

The S3 and Azure helpers always download to /tmp/<file name>. The Hugging Face helper downloads to /tmp/<repo_id> unless you set local_dir. Keep these points in mind:
  • The SDK does not delete downloaded files after the scan. Remove them yourself if disk space or data retention matters.
  • Because the paths are hard-coded, the S3 and Azure helpers may not work on Windows. This needs confirmation. On Windows, consider downloading the file yourself and calling scan_file, or setting local_dir for Hugging Face.

Scan a remote model with the community scanner

Use client.community_scanner.community_scan when HiddenLayer can reach the model directly, for example through a presigned URL or a public Hugging Face repository. HiddenLayer fetches the model itself, so nothing is downloaded to or uploaded from your machine. Set model_source to one of the CommunityScanSource constants:
The SDK defines these constants, but this page does not document what access setup each source type requires on the HiddenLayer side (for example, which IAM role HiddenLayer assumes). This needs confirmation. The HUGGING_FACE example below follows the SDK’s own usage.
community_scan.py
Expected result: a report for the remote model. The repository in this example is intentionally vulnerable, so expect detections.

Start a scan without waiting

By default, scan methods block until the scan finishes. Pass wait_for_results=False to return as soon as the scan starts. This is useful in web services or pipelines where you do not want to hold a process open. With wait_for_results=False:
  • For upload-based methods, the upload itself still finishes before the method returns. Only the wait for the analysis is skipped.
  • The returned report is a point-in-time snapshot. Its status is often pending or running, and file_results may be incomplete.
  • Save report.scan_id, then call scan_result(scan_id=...) later to fetch an updated report. Both client.model_scanner and client.community_scanner provide scan_result.
start_then_fetch.py
If the new scan is not visible yet, the SDK retries a few times. It raises hiddenlayer.NotFoundError if the scan still cannot be found.

Wait with a time limit

When wait_for_results=True, the SDK polls the scan status with exponential backoff (up to 30 seconds between checks) until the scan is done, failed, or canceled. There is no overall timeout. If a scan stays in pending or running, your program waits indefinitely. In CI/CD or other unattended environments, start the scan with wait_for_results=False and poll yourself with a deadline. client.scans.results.retrieve_summary(scan_id) is a lightweight call that returns the status and summary without the per-file results.
scan_with_timeout.py
Expected result: the script either prints the final status and highest severity, or exits with a timeout message that includes the scan ID so you can check it later. For lower-level scan APIs, such as listing scan jobs, paging through file results, and downloading results in SARIF format (Static Analysis Results Interchange Format, a JSON format that many code-scanning tools accept), see API usage examples.

Common errors and fixes

The helper you called needs a third-party package that the SDK does not install. Install the package listed in Before you begin, for example pip install boto3.
HiddenLayer rejected your credentials. Check that HIDDENLAYER_CLIENT_ID and HIDDENLAYER_CLIENT_SECRET are set in the same shell or process that runs the script. Also check that the environment matches your account’s region (prod-us or prod-eu). See Authentication and configuration.
Your credentials are valid but lack access to model scanning. Ask a Console administrator to check the API key’s permissions.
The S3 or Azure download failed before the scan started. The message includes the underlying error. Check the bucket, key, container, and blob names, and confirm that your AWS or Azure credentials can read the object.
model_path and path are resolved relative to the current working directory. Use an absolute path, or run the script from the expected directory.
Failed scans are returned, not raised. Inspect file_error on each entry in report.file_results for scanner error messages, and check report.summary.files_failed_to_scan.
wait_for_results=True has no overall timeout. Use the pattern in Wait with a time limit.
With wait_for_results=False, the SDK retries a few times while a new scan becomes available. If it still raises NotFoundError, wait briefly and call scan_result(scan_id=...) again. Also confirm you are using the same environment (region) that started the scan.
For more on exceptions and retries, see Error handling and Troubleshooting.