File fingerprinting

Learn about Debricked's fingerprint analysis and how to set it up.

This feature is only available for SCA Enterprise users. Already have an account? Click here to upgrade.

Currently supported languages: C#, Go, Java, Python.

Debricked supports scanning for unmanaged dependencies not defined in manifest-files by examining fingerprints of the contents (including binary files) of your application (with some exclusions for non-relevant files).

Enabling file fingerprinting

To enable file fingerprint analysis, first you need to generate a debricked.fingerprints.txt file. This can be done using the Debricked CLI fingerprint command, which can be added to your integration, so that it is generated appropriately before making a Debricked scan. Ensure that your application has been built prior to running the fingerprint command, to ensure that the relevant dependencies are included in the scan. By default, we do not unpack archive files such as .jar, .nupkg, and .war, but it can be activated through the —fingerprint-compressed-content flag. To find out more about which files are unpacked with this file and more information on the command and the various available flags, run:

debricked fingerprint -h

File fingerprinting is run by default as part of the scan command (since CLI v2.0.10), but this can be disabled by running debricked scan --no-fingerprint=true.

Example configuration

Here’s an example of how to run the fingerprint command directly prior to the scan. The command can be run in any stage of your pipeline, as long as the debricked.fingerprints.txt file is available to be picked up when running debricked scan.

# GitLab CI/CD template

image: debricked/cli:2-resolution-debian

stages:
  - scan
debricked:
  stage: scan
  script:
    - debricked fingerprint
    - debricked scan --no-fingerprint=true

The algorithm

The algorithm is built to make the match for each file in two stages: package matching and version resolution.

In the initial stage, we consider both the first occurrence of all packages which matches the given file hash and the path used. Based of those two parameters, we assign a package to each file included in your fingerprints. The exact version is to be decided in the next part of the algorithm, as this particular file may exist in multiple versions of this package.

To decide the version or versions for a set of matches that are assigned to a specific package, we optimize to find the version with the most matches. This is an iterative algorithm, so we run this part of the algorithm until all files have resolved a specific version. We prioritize stable versions with higher release dates first if they have the same amount of matches.

Manage or override results

If you believe Debricked has found a wrong dependency and you'd like to change matches, it is possible to manage and override the results.

In some instances, the package and/or version resulting from file fingerprinting may differ from the dependency used in your application. One way of ensuring the results are correct is excluding fingerprinting of a certain file or path. This can be achieved through the scan command by using the --exclusion flag and adding the correct dependency to a manifest file or a CycloneDX SBOM included in your scan.

If you instead wish to override these results without adding them to a dependency file, this can be done natively through the debricked-config.yaml file, with the CLI.

The overriding can be performed per file hash, set of file hashes, folders, and more. This is determined by an array called fileRegexes. If a file path matches a given Rust-flavoured regex, you can determine the pURL and version (if specified) that will be set to the matches made. Entries higher in the list have higher priority, so the first matching fileRegex for a given file path will always be taken.

Here is an example of the debricked-config.yaml format:

overrides:
  - pURL: "pkg:npm/lodash"
    version: "1.0.0" # (optional: if left out, we will determine the version)
    fileRegexes: # (Rust regex)
      - "@types/lodash/.*"
  - pURL: "pkg:maven/org.openjfx/javafx-base"
    version: false # false means that we will determine the version
    fileRegexes:
      - "subpath/org.openjfx/.*"
  - pURL: "pkg:maven/junit/junit"
    fileRegexes:
      - "junit-3.8.1/junit-3.8.1.jar"
      - "junit-4.1/junit-4.1.jar"

If you want to try your regex, you could visit https://regex101.com and choose Rust as the flavour.

Packages match using the pURL of the package that you’ve imported. We use the common pURL format as per the pURL spec:

pkg:type/namespace/name
  • The scheme section is the URL scheme with the constant value of pkg.

  • The type section of the pURL corresponds to the package manager used by the dependency or the version control system used (in case of repository-only dependencies).

  • The namespace is used mainly for repository dependencies corresponding to the repository owner or dependencies with vendor.

  • The name section corresponds to the package or repository’s name.

Here are some examples of pURL searches and their corresponding results:

  • pkg:pypi/tensorflow → Tensorflow

  • pkg:npm/react → React

  • pkg:maven/org.springframework.boot/spring-boot-starter-web

Have a look at the pURL spec page to learn more.

Last updated