pax_global_header00006660000000000000000000000064152612035610014513gustar00rootroot0000000000000052 comment=b254e6d92f28c3868a755f62fb3ca8f26e9fee76 syft-1.54.1/000077500000000000000000000000001526120356100125705ustar00rootroot00000000000000syft-1.54.1/.binny.yaml000066400000000000000000000016231526120356100146530ustar00rootroot00000000000000# only pull in version updates that were released more than a week ago (low-pass filter for quickly-retracted releases) cooldown: 7d # Most tools (binny, chronicle, cosign, golangci-lint, goreleaser, gosimports, # bouncer, quill, syft, task, gh) are inherited from anchore/go-make's embedded # .binny.yaml — see https://github.com/anchore/go-make. Only syft-specific tools # or version overrides should live here. tools: # used in integration tests to verify JSON schemas - name: yajsv version: want: v1.4.1 method: github-release with: repo: neilpa/yajsv # used to upload test fixture cache - name: oras version: want: v1.3.4 method: github-release with: repo: oras-project/oras # used to parse JSON/YAML annotations on the fixture cache image - name: yq version: want: v4.53.6 method: github-release with: repo: mikefarah/yq syft-1.54.1/.bouncer.yaml000066400000000000000000000041531526120356100151720ustar00rootroot00000000000000permit: - BSD.* - CC0.* - MIT.* - Apache.* - MPL.* - ISC - WTFPL - Unlicense ignore-packages: # https://github.com/sorairolake/lzip-go/blob/34a2615d2abf740175c6b0a835baa08364e09430/go.sum.license#L3 # has `SPDX-License-Identifier: Apache-2.0 OR MIT`, both of which are acceptable - github.com/sorairolake/lzip-go # packageurl-go is released under the MIT license located in the root of the repo at /mit.LICENSE - github.com/anchore/packageurl-go # both of these dependencies are specified as Apache-2.0 in their respective GitHub READMEs - github.com/alibabacloud-go/cr-20160607/client - github.com/alibabacloud-go/tea-xml/service # crypto/internal/boring is released under the openSSL license as a part of the Golang Standard Libary - crypto/internal/boring # from: https://github.com/spdx/tools-golang/blob/main/LICENSE.code # The tools-golang source code is provided and may be used, at your option, # under either: # * Apache License, version 2.0 (Apache-2.0), OR # * GNU General Public License, version 2.0 or later (GPL-2.0-or-later). # (we choose Apache-2.0) - github.com/spdx/tools-golang # from: https://github.com/xi2/xz/blob/master/LICENSE # All these files have been put into the public domain. # You can do whatever you want with these files. - github.com/xi2/xz # from: https://gitlab.com/cznic/sqlite/-/blob/v1.15.4/LICENSE # This is a BSD-3-Clause license - modernc.org/libc - modernc.org/libc/errno - modernc.org/libc/fcntl - modernc.org/libc/fts - modernc.org/libc/grp - modernc.org/libc/langinfo - modernc.org/libc/limits - modernc.org/libc/netdb - modernc.org/libc/netinet/in - modernc.org/libc/poll - modernc.org/libc/pthread - modernc.org/libc/pwd - modernc.org/libc/signal - modernc.org/libc/stdio - modernc.org/libc/stdlib - modernc.org/libc/sys/socket - modernc.org/libc/sys/stat - modernc.org/libc/sys/types - modernc.org/libc/termios - modernc.org/libc/time - modernc.org/libc/unistd - modernc.org/libc/utime - modernc.org/libc/uuid/uuid - modernc.org/libc/wctype - modernc.org/mathutil - modernc.org/memory syft-1.54.1/.chronicle.yaml000066400000000000000000000027541526120356100155100ustar00rootroot00000000000000enforce-v0: false title: "" github: host: github.com include-issue-pr-authors: true include-issue-prs: true include-issues-not-planned: false include-prs: true include-issues: true include-unlabeled-issues: true include-unlabeled-prs: true issues-require-linked-prs: false consider-pr-merge-commits: true exclude-labels: - duplicate - question - invalid - wontfix - wont-fix - release-ignore - changelog-ignore - ignore changes: - name: security-fixes title: Security Fixes semver-field: patch labels: - security - vulnerability - name: added-feature title: Added Features semver-field: minor labels: - enhancement - feature - minor - name: bug-fix title: Bug Fixes semver-field: patch labels: - bug - fix - bug-fix - patch - name: breaking-feature title: Breaking Changes semver-field: major labels: - breaking - backwards-incompatible - breaking-change - breaking-feature - major - detected-breaking-change - name: removed-feature title: Removed Features semver-field: major labels: - removed - name: deprecated-feature title: Deprecated Features semver-field: minor labels: - deprecated - name: unknown title: Additional Changes semver-field: "" labels: [] syft-1.54.1/.gitattributes000066400000000000000000000006121526120356100154620ustar00rootroot00000000000000# check out text files with lf on every platform (regardless of core.autocrlf), letting git detect binary files * text=auto eol=lf # fixtures that must keep their crlf line endings byte for byte syft/linux/testdata/unprintable -text syft/pkg/cataloger/bitnami/testdata/no-rel/opt/bitnami/redis/.spdx-redis.spdx -text syft/pkg/cataloger/java/testdata/jar-metadata/**/META-INF/MANIFEST.MF -text syft-1.54.1/.github/000077500000000000000000000000001526120356100141305ustar00rootroot00000000000000syft-1.54.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001526120356100163135ustar00rootroot00000000000000syft-1.54.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000005021526120356100210020ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **What happened**: **What you expected to happen**: **Steps to reproduce the issue**: **Anything else we need to know?**: **Environment**: - Output of `syft version`: - OS (e.g: `cat /etc/os-release` or similar): syft-1.54.1/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000003751526120356100203100ustar00rootroot00000000000000contact_links: - name: Join our Discourse community šŸ’¬ # link to our community Discourse site url: https://anchore.com/discourse about: 'Come chat with us! Ask for help, join our software development efforts, or just give us feedback!' syft-1.54.1/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000004311526120356100220360ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **What would you like to be added**: **Why is this needed**: **Additional context**: syft-1.54.1/.github/actions/000077500000000000000000000000001526120356100155705ustar00rootroot00000000000000syft-1.54.1/.github/actions/bootstrap/000077500000000000000000000000001526120356100176055ustar00rootroot00000000000000syft-1.54.1/.github/actions/bootstrap/action.yaml000066400000000000000000000065621526120356100217570ustar00rootroot00000000000000name: "Bootstrap" description: "Bootstrap all syft tools and dependencies on top of go-make's setup action" # This action is a thin wrapper around anchore/go-make/.github/actions/setup which # already handles checkout, setup-go, restore-only build/mod cache, and tool cache. # We add the syft-specific extras here: apt packages and the test fixture cache. inputs: go-version: description: "Go version to install (passed to go-make/setup)" required: true default: "1.26.2" cache-key-prefix: description: "Prefix all cache keys with this value (passed to go-make/setup)" required: true default: "v1" cache-enabled: description: "Enable build/mod and tool caching (passed to go-make/setup)" required: true default: "true" download-test-fixture-cache: description: "Download test fixture cache from OCI and github actions" required: true default: "false" bootstrap-apt-packages: description: "Space delimited list of tools to install via apt" default: "libxml2-utils" runs: using: "composite" steps: - name: Setup go + go-make tooling uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1 with: go-version: ${{ inputs.go-version }} cache-key-prefix: ${{ inputs.cache-key-prefix }} cache-enabled: ${{ inputs.cache-enabled }} - name: Install binny-managed tools shell: bash run: make binny:install - name: Install apt packages if: inputs.bootstrap-apt-packages != '' shell: bash env: APT_PACKAGES: ${{ inputs.bootstrap-apt-packages }} run: | IFS=' ' read -ra packages <<< "$APT_PACKAGES" # the AMI already ships most of these, and `apt update` costs ~40MB of index (minutes, # when archive.ubuntu.com throttles us) to find that out. only touch apt if we must. missing=() for pkg in "${packages[@]}"; do dpkg -s "$pkg" >/dev/null 2>&1 || missing+=("$pkg") done if [ ${#missing[@]} -eq 0 ]; then echo "all apt packages already present: ${packages[*]}" exit 0 fi DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y "${missing[@]}" # ORAS cache: restore-only on non-default branches / forks - name: Restore ORAS cache from github actions if: ${{ inputs.download-test-fixture-cache == 'true' && (github.ref != format('refs/heads/{0}', github.event.repository.default_branch) || github.event.repository.fork == true) }} uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ github.workspace }}/.tmp/oras-cache key: ${{ inputs.cache-key-prefix }}-oras-cache # ORAS cache: restore + save on the default branch of the canonical repo only. - name: Restore and save ORAS cache from github actions if: ${{ inputs.download-test-fixture-cache == 'true' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.event.repository.fork == false }} uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ github.workspace }}/.tmp/oras-cache key: ${{ inputs.cache-key-prefix }}-oras-cache - name: Download test fixture cache if: inputs.download-test-fixture-cache == 'true' shell: bash run: make download-test-fixture-cache syft-1.54.1/.github/dependabot.yml000066400000000000000000000017731526120356100167700ustar00rootroot00000000000000# Dependabot configuration version: 2 updates: - package-ecosystem: gomod directories: - "/" - "/.make" cooldown: default-days: 7 schedule: interval: "weekly" day: "friday" open-pull-requests-limit: 10 labels: - "dependencies" ignore: - dependency-name: "github.com/aquasecurity/go-pep440-version" - dependency-name: "github.com/aquasecurity/go-version" - dependency-name: "github.com/knqyf263/go-apk-version" - dependency-name: "github.com/knqyf263/go-deb-version" # v1 changed SlicesAsSets hashing, which changes every artifact ID syft produces - dependency-name: "github.com/gohugoio/hashstructure" update-types: ["version-update:semver-major"] - package-ecosystem: "github-actions" directories: - "/" - "/.github/actions/*" cooldown: default-days: 7 schedule: interval: "weekly" day: "friday" open-pull-requests-limit: 10 labels: - "dependencies" syft-1.54.1/.github/pull_request_template.md000066400000000000000000000023331526120356100210720ustar00rootroot00000000000000## Description ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (please discuss with the team first; Syft is 1.0 software and we won't accept breaking changes without going to 2.0) - [ ] Documentation (updates the documentation) - [ ] Chore (improve the developer experience, fix a test flake, etc, without changing the visible behavior of Syft) - [ ] Performance (make Syft run faster or use less memory, without changing visible behavior much) ## Checklist - [ ] I have added unit tests that cover changed behavior - [ ] I have tested my code in common scenarios and confirmed there are no regressions - [ ] I have added comments to my code, particularly in hard-to-understand sections ## Issue references syft-1.54.1/.github/scripts/000077500000000000000000000000001526120356100156175ustar00rootroot00000000000000syft-1.54.1/.github/scripts/capability-drift-check.sh000077500000000000000000000007611526120356100224640ustar00rootroot00000000000000#!/usr/bin/env bash set -u if [ "$(git status --porcelain | wc -l)" -ne "0" ]; then echo " šŸ”“ there are uncommitted changes, please commit them before running this check" exit 1 fi if ! make generate-capabilities REFRESH=false; then echo "Generating capability descriptions failed" exit 1 fi if [ "$(git status --porcelain | wc -l)" -ne "0" ]; then echo " šŸ”“ there is drift in capability descriptions! Please run 'make generate-capabilities' and commit the changes." exit 1 fisyft-1.54.1/.github/scripts/check_binary_fixture_size.sh000077500000000000000000000013641526120356100234030ustar00rootroot00000000000000#!/bin/bash # current limit for fixture size size=1000 if [ $# -eq 0 ]; then echo "Usage: $0 " exit 1 fi directory="$1" # Remove trailing slash using parameter expansion directory="${directory%/}" if [ ! -d "$directory" ]; then echo "Directory not found: $directory" exit 1 fi found_large_files=0 while IFS= read -r -d '' file; do if [ $(wc -c < "$file") -gt $size ]; then echo "File $file is greater than ${size} bytes." found_large_files=1 fi done < <(git ls-files -z "$directory") if [ "$found_large_files" -eq 1 ]; then echo "Script failed: Some files are greater than ${size} bytes." exit 1 else echo "All files in $directory and its subdirectories are ${size} bytes or smaller. Check passed." exit 0 fi syft-1.54.1/.github/scripts/find_cache_paths.py000077500000000000000000000110601526120356100214340ustar00rootroot00000000000000#!/usr/bin/env python3 from __future__ import annotations import glob import hashlib import json import os import sys IGNORED_PREFIXES = [] def find_fingerprints_and_check_dirs(base_dir): all_fingerprints = set( glob.glob( os.path.join(base_dir, "**", "test*", "**", "*.fingerprint"), recursive=True ) ) all_fingerprints = { os.path.relpath(fp) for fp in all_fingerprints if not any(fp.startswith(prefix) for prefix in IGNORED_PREFIXES) } if not all_fingerprints: show("No .fingerprint files or cache directories found.") exit(1) orphan_fingerprints = [] empty_content = [] valid_paths = set() fingerprint_contents = [] for fingerprint in all_fingerprints: path = fingerprint.replace(".fingerprint", "") if not os.path.exists(path): # paired content path is entirely missing — the .fingerprint is likely # leftover from a moved/deleted source (testdata trees are git-ignored, # so they persist locally across rename refactors) orphan_fingerprints.append(fingerprint) continue if not os.path.isdir(path): valid_paths.add(path) continue if os.listdir(path): valid_paths.add(path) else: empty_content.append(path) with open(fingerprint, "r") as f: content = f.read().strip() fingerprint_contents.append((fingerprint, content)) return sorted(valid_paths), empty_content, orphan_fingerprints, fingerprint_contents def parse_fingerprint_contents(fingerprint_content): input_map = {} for line in fingerprint_content.splitlines(): digest, path = line.split() input_map[path] = digest return input_map def calculate_sha256(fingerprint_contents): sorted_fingerprint_contents = sorted(fingerprint_contents, key=lambda x: x[0]) concatenated_contents = "".join( content for _, content in sorted_fingerprint_contents ) sha256_hash = hashlib.sha256(concatenated_contents.encode()).hexdigest() return sha256_hash def calculate_file_sha256(file_path): sha256_hash = hashlib.sha256() with open(file_path, "rb") as f: for byte_block in iter(lambda: f.read(4096), b""): sha256_hash.update(byte_block) return sha256_hash.hexdigest() def show(*s: str): print(*s, file=sys.stderr) def main(file_path: str | None): base_dir = "." valid_paths, empty_content, orphan_fingerprints, fingerprint_contents = ( find_fingerprints_and_check_dirs(base_dir) ) if empty_content: show( "The following paths exist but are empty, and have corresponding .fingerprint files:" ) for path in sorted(empty_content): show(f"- {path}") # when adding new cache directories there is a time where it is not possible to have this directory without # running the tests first... but this step is a prerequisite for running the tests. We should not block on this. if orphan_fingerprints: show( "The following .fingerprint files reference paths that no longer exist " "(likely leftover from a moved/deleted cataloger — safe to delete, " "or run `task prune-orphan-fingerprints`):" ) for fp in sorted(orphan_fingerprints): show(f"- {fp}") sha256_hash = calculate_sha256(fingerprint_contents) paths_with_digests = [] for path in sorted(valid_paths): fingerprint_file = f"{path}.fingerprint" try: if os.path.exists(fingerprint_file): file_digest = calculate_file_sha256(fingerprint_file) # Parse the fingerprint file to get the digest/path tuples with open(fingerprint_file, "r") as f: fingerprint_content = f.read().strip() input_map = parse_fingerprint_contents(fingerprint_content) paths_with_digests.append( {"path": path, "digest": file_digest, "input": input_map} ) except Exception as e: show(f"Error processing {fingerprint_file}: {e}") raise e output = {"digest": sha256_hash, "paths": paths_with_digests} content = json.dumps(output, indent=2, sort_keys=True) if file_path: with open(file_path, "w") as f: f.write(content) print(content) if __name__ == "__main__": file_path = None if len(sys.argv) > 1: file_path = sys.argv[1] main(file_path) syft-1.54.1/.github/scripts/fingerprint_docker_fixtures.py000077500000000000000000000043611526120356100240070ustar00rootroot00000000000000#!/usr/bin/env python3 import os import subprocess import hashlib BOLD = '\033[1m' YELLOW = '\033[0;33m' RESET = '\033[0m' def print_message(message): print(f"{YELLOW}{message}{RESET}") def sha256sum(filepath): h = hashlib.sha256() with open(filepath, 'rb') as f: for chunk in iter(lambda: f.read(4096), b""): h.update(chunk) return h.hexdigest() def is_git_tracked_or_untracked(directory): """Returns a sorted list of files in the directory that are tracked or not ignored by Git.""" result = subprocess.run( ["git", "ls-files", "--cached", "--others", "--exclude-standard"], cwd=directory, stdout=subprocess.PIPE, text=True ) return sorted(result.stdout.strip().splitlines()) def find_test_fixture_dirs_with_images(base_dir): """Find directories that contain 'testdata' and at least one 'image-*' directory.""" for root, dirs, files in os.walk(base_dir): if 'testdata' in root: image_dirs = [d for d in dirs if d.startswith('image-')] if image_dirs: yield os.path.realpath(root) def generate_fingerprints(): print_message("creating fingerprint files for docker fixtures...") for test_fixture_dir in find_test_fixture_dirs_with_images('.'): cache_fingerprint_path = os.path.join(test_fixture_dir, 'cache.fingerprint') with open(cache_fingerprint_path, 'w') as fingerprint_file: for image_dir in find_image_dirs(test_fixture_dir): for file in is_git_tracked_or_untracked(image_dir): file_path = os.path.join(image_dir, file) checksum = sha256sum(file_path) path_from_fixture_dir = os.path.relpath(file_path, test_fixture_dir) fingerprint_file.write(f"{checksum} {path_from_fixture_dir}\n") def find_image_dirs(test_fixture_dir): """Find all 'image-*' directories inside a given test-fixture directory.""" result = [] for root, dirs, files in os.walk(test_fixture_dir): for dir_name in dirs: if dir_name.startswith('image-'): result.append(os.path.join(root, dir_name)) return sorted(result) if __name__ == "__main__": generate_fingerprints() syft-1.54.1/.github/scripts/json-schema-drift-check.sh000077500000000000000000000007121526120356100225460ustar00rootroot00000000000000#!/usr/bin/env bash set -u if [ "$(git status --porcelain | wc -l)" -ne "0" ]; then echo " šŸ”“ there are uncommitted changes, please commit them before running this check" exit 1 fi if ! make generate-json-schema; then echo "Generating json schema failed" exit 1 fi if [ "$(git status --porcelain | wc -l)" -ne "0" ]; then echo " šŸ”“ there is drift in json schema! Please run 'make generate-json-schema' and commit the changes." exit 1 fi syft-1.54.1/.github/scripts/labeler.py000077500000000000000000000220611526120356100176030ustar00rootroot00000000000000#!/usr/bin/env python3 from __future__ import annotations import sys import glob import subprocess import os import re DRY_RUN = False JSON_SCHEMA_LABEL = "json-schema" # note: we can't use "breaking-change" as the label since that might be applied manually by a user. This is a # distinct label that we can use to indicate that the label was applied (or removed) by automation. BREAKING_CHANGE_LABEL = "detected-breaking-change" def main(changed_files: str | None = None, merge_base_schema_files: str | None = None): global DRY_RUN pr_number = os.environ.get("GITHUB_PR_NUMBER") comment_file_path = os.environ.get("CI_COMMENT_FILE") if not comment_file_path: print("CI_COMMENT_FILE not set") sys.exit(1) if not pr_number: DRY_RUN = True if changed_files: DRY_RUN = True # read lines from file... this is useful for local testing with open(changed_files) as f: pr_changed_files = f.read().splitlines() with open(merge_base_schema_files) as f: og_json_schema_files = sort_json_schema_files(f.read().splitlines()) else: if not is_ci(): print("Not in CI") sys.exit(1) if not pr_number: print("Not a PR") sys.exit(1) pr_changed_files = get_pr_changed_files(pr_number) # since we are running this in the context of the pull_request_target, the checkout is the merge base.. # that is the main branch of the original repo, NOT the branch in the forked repo (or branch in the target # repo for non-forked PRs). This means we just need to list the current checkedout files to get a sense of # the changes before a merge. og_json_schema_files = list_json_schema_files() pr_json_schema_files = filter_to_schema_files(pr_changed_files) pr_labels = get_pr_labels(pr_number) # print("schema files in pr: ", summarize_schema_files(pr_json_schema_files)) # print("og schema files: ", summarize_schema_files(og_json_schema_files)) if not og_json_schema_files: print("No schema files found in merge base") sys.exit(1) # pr_json_schema_files = set of PR files are added, removed, and changed files new_schema_files = set(pr_json_schema_files) - set(og_json_schema_files) removed_or_modified_schema_files = set(pr_json_schema_files) - set(new_schema_files) print("new schemas: ", summarize_schema_files(new_schema_files)) print("removed or modified schemas:", summarize_schema_files(removed_or_modified_schema_files)) # if there is a new or modified schema, we should add the "json-schema" label to the PR... if new_schema_files or removed_or_modified_schema_files: print("\nAdding json-schema label...") add_label(pr_number, JSON_SCHEMA_LABEL) else: if JSON_SCHEMA_LABEL in pr_labels: remove_label(pr_number, JSON_SCHEMA_LABEL) # new schema files should be scrutinized, comparing the latest and added versions to see if it's a breaking # change (major version bump). Warn about it on the PR via adding a breaking-change label... if is_breaking_change(new_schema_files, og_json_schema_files[-1]): print("\nBreaking change detected...") add_label(pr_number, BREAKING_CHANGE_LABEL) else: if BREAKING_CHANGE_LABEL in pr_labels: remove_label(pr_number, BREAKING_CHANGE_LABEL) # modifying an existing schema could be a breaking change, we should warn about it on the PR via a comment... # removing schema files should never be allowed, we should warn about it on the PR via a comment... if removed_or_modified_schema_files: print("\nRemoved or modified schema detected...") schemas = sort_json_schema_files(list(removed_or_modified_schema_files)) schemas_str = "\n".join([f" - {schema}" for schema in schemas]) add_comment(comment_file_path, f"Detected modification or removal of existing json schemas:\n{schemas_str}", warning=True) def add_comment(comment_file_path: str, comment: str, warning: bool = False, important: bool = False): if warning or important: comment_lines = comment.splitlines() comment = "\n".join([f"> {line}" for line in comment_lines]) if warning: comment = f"> [!WARNING]\n{comment}" elif important: comment = f"> [!IMPORTANT]\n{comment}" # create any parent directories if they don't exist os.makedirs(os.path.dirname(comment_file_path), exist_ok=True) with open(comment_file_path, "w") as f: f.write(comment) print(f"Comment file contents: {comment_file_path}") print(comment) def add_label(pr_number: str, label: str): # run "gh pr edit --add-label