From a9422fbf4eaa869a265331f2de5d2748741b9e69 Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Tue, 25 Nov 2025 13:20:34 -0800 Subject: [PATCH] Migrate `LinuxScanner` from `Newtonsoft.Json` to `System.Text.Json` - `LinuxScanner`: - Replace `JsonConvert.SerializeObject` with `JsonSerializer.Serialize` - Replace `JsonConvert.DeserializeObject` with `SyftOutput.FromJson()` - Remove `Newtonsoft.Json` using directive - `SyftOutput`: - Regenerate from JSON schema using quicktype.io with `System.Text.Json` - Add `[JsonPropertyName]` attributes for proper property mapping - Add `[JsonIgnore]` attributes for null value handling - Add `FromJson()` helper method with case-insensitive parsing - Enable nullable reference types with appropriate pragmas - `LinuxScannerTests`: - Convert test JSON strings from escaped C# strings to raw literals - Fix property name casing to match schema (`versionID` vs `versionId`) - Remove trailing commas that were previously tolerated See #231 --- .../linux/Contracts/SyftOutput.cs | 3193 ++++++++++++++++- .../linux/LinuxScanner.cs | 8 +- .../LinuxScannerTests.cs | 558 +-- 3 files changed, 3474 insertions(+), 285 deletions(-) diff --git a/src/Microsoft.ComponentDetection.Detectors/linux/Contracts/SyftOutput.cs b/src/Microsoft.ComponentDetection.Detectors/linux/Contracts/SyftOutput.cs index 2f0fd3f77..ae185e3df 100644 --- a/src/Microsoft.ComponentDetection.Detectors/linux/Contracts/SyftOutput.cs +++ b/src/Microsoft.ComponentDetection.Detectors/linux/Contracts/SyftOutput.cs @@ -1,576 +1,3251 @@ -#nullable disable - // Take schema from https://github.com/anchore/syft/tree/main/schema/json. // Match version to tag used i.e. https://github.com/anchore/syft/blob/v1.37.0/internal/constants.go#L6 // Can convert JSON Schema to C# using quicktype.io. // (change name of top Coordinate class to SyftOutput) // +#nullable enable +#pragma warning disable CS8618 +#pragma warning disable CS8601 +#pragma warning disable CS8603 namespace Microsoft.ComponentDetection.Detectors.Linux.Contracts; using System; using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Globalization; + +/// +/// Document represents the syft cataloging findings as a JSON document +/// public partial class SyftOutput { + [JsonPropertyName("artifactRelationships")] public ArtifactRelationshipElement[] ArtifactRelationships { get; set; } + + [JsonPropertyName("artifacts")] public ArtifactElement[] Artifacts { get; set; } + + [JsonPropertyName("descriptor")] public Descriptor Descriptor { get; set; } + + [JsonPropertyName("distro")] public Distro Distro { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("files")] public FileElement[] Files { get; set; } + + [JsonPropertyName("schema")] public Schema Schema { get; set; } + + [JsonPropertyName("source")] public SourceClass Source { get; set; } } public partial class ArtifactRelationshipElement { + [JsonPropertyName("child")] public string Child { get; set; } + + [JsonPropertyName("metadata")] public object Metadata { get; set; } + + [JsonPropertyName("parent")] public string Parent { get; set; } + + [JsonPropertyName("type")] public string Type { get; set; } } +/// +/// Package represents a pkg.Package object specialized for JSON marshaling and unmarshalling. +/// public partial class ArtifactElement { + [JsonPropertyName("cpes")] public CpeElement[] Cpes { get; set; } + + [JsonPropertyName("foundBy")] public string FoundBy { get; set; } + + [JsonPropertyName("id")] public string Id { get; set; } + + [JsonPropertyName("language")] public string Language { get; set; } + + [JsonPropertyName("licenses")] public ArtifactLicense[] Licenses { get; set; } + + [JsonPropertyName("locations")] public LocationElement[] Locations { get; set; } + + [JsonPropertyName("metadata")] public MetadataClass Metadata { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("metadataType")] public string MetadataType { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("purl")] public string Purl { get; set; } + + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("version")] public string Version { get; set; } } public partial class CpeElement { + [JsonPropertyName("cpe")] public string Cpe { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("source")] public string Source { get; set; } } public partial class ArtifactLicense { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("contents")] public string Contents { get; set; } + + [JsonPropertyName("locations")] public LocationElement[] Locations { get; set; } + + [JsonPropertyName("spdxExpression")] public string SpdxExpression { get; set; } + + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("urls")] public string[] Urls { get; set; } + + [JsonPropertyName("value")] public string Value { get; set; } } +/// +/// Location represents a path relative to a particular filesystem resolved to a specific +/// file.Reference. +/// public partial class LocationElement { + /// + /// AccessPath is the path used to retrieve file contents (which may or may not have + /// hardlinks / symlinks in the path) + /// + [JsonPropertyName("accessPath")] public string AccessPath { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("annotations")] public Dictionary Annotations { get; set; } + + /// + /// FileSystemID is an ID representing and entire filesystem. For container images, this is a + /// layer digest. For directories or a root filesystem, this is blank. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("layerID")] public string LayerId { get; set; } + + /// + /// RealPath is the canonical absolute form of the path accessed (all symbolic links have + /// been followed and relative path components like '.' and '..' have been removed). + /// + [JsonPropertyName("path")] public string Path { get; set; } } +/// +/// AlpmDBEntry is a struct that represents the package data stored in the pacman flat-file +/// stores for arch linux. +/// +/// ApkDBEntry represents all captured data for the alpine linux package manager flat-file +/// store. +/// +/// BinarySignature represents a set of matched values within a binary file. +/// +/// BitnamiSBOMEntry represents all captured data from Bitnami packages described in Bitnami' +/// SPDX files. +/// +/// ConanfileEntry represents a single "Requires" entry from a conanfile.txt. +/// +/// ConaninfoEntry represents a single "full_requires" entry from a conaninfo.txt. +/// +/// ConanV1LockEntry represents a single "node" entry from a conan.lock V1 file. +/// +/// ConanV2LockEntry represents a single "node" entry from a conan.lock V2 file. +/// +/// CocoaPodfileLockEntry represents a single entry from the "Pods" section of a Podfile.lock +/// file. +/// +/// CondaMetaPackage represents metadata for a Conda package extracted from the +/// conda-meta/*.json files. +/// +/// DartPubspec is a struct that represents a package described in a pubspec.yaml file +/// +/// DartPubspecLockEntry is a struct that represents a single entry found in the "packages" +/// section in a Dart pubspec.lock file. +/// +/// DotnetDepsEntry is a struct that represents a single entry found in the "libraries" +/// section in a .NET [*.]deps.json file. +/// +/// DotnetPackagesLockEntry is a struct that represents a single entry found in the +/// "dependencies" section in a .NET packages.lock.json file. +/// +/// DotnetPortableExecutableEntry is a struct that represents a single entry found within +/// "VersionResources" section of a .NET Portable Executable binary file. +/// +/// DpkgArchiveEntry represents package metadata extracted from a .deb archive file. +/// +/// DpkgDBEntry represents all captured data for a Debian package DB entry; available fields +/// are described at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the +/// --showformat section. +/// +/// ELFBinaryPackageNoteJSONPayload Represents metadata captured from the .note.package +/// section of an ELF-formatted binary +/// +/// ElixirMixLockEntry is a struct that represents a single entry in a mix.lock file +/// +/// ErlangRebarLockEntry represents a single package entry from the "deps" section within an +/// Erlang rebar.lock file. +/// +/// GitHubActionsUseStatement represents a single 'uses' statement in a GitHub Actions +/// workflow file referencing an action or reusable workflow. +/// +/// GolangBinaryBuildinfoEntry represents all captured data for a Golang binary +/// +/// GolangModuleEntry represents all captured data for a Golang source scan with +/// go.mod/go.sum +/// +/// GolangSourceEntry represents all captured data for a Golang package found through source +/// analysis +/// +/// HackageStackYamlEntry represents a single entry from the "extra-deps" section of a +/// stack.yaml file. +/// +/// HackageStackYamlLockEntry represents a single entry from the "packages" section of a +/// stack.yaml.lock file. +/// +/// HomebrewFormula represents metadata about a Homebrew formula package extracted from +/// formula JSON files. +/// +/// JavaArchive encapsulates all Java ecosystem metadata for a package as well as an +/// (optional) parent relationship. +/// +/// JavaVMInstallation represents a Java Virtual Machine installation discovered on the +/// system with its release information and file list. +/// +/// NpmPackage represents the contents of a javascript package.json file. +/// +/// NpmPackageLockEntry represents a single entry within the "packages" section of a +/// package-lock.json file. +/// +/// YarnLockEntry represents a single entry section of a yarn.lock file. +/// +/// LinuxKernel represents all captured data for a Linux kernel +/// +/// LinuxKernelModule represents a loadable kernel module (.ko file) with its metadata, +/// parameters, and dependencies. +/// +/// LuaRocksPackage represents a Lua package managed by the LuaRocks package manager with +/// metadata from .rockspec files. +/// +/// MicrosoftKbPatch is slightly odd in how it is expected to map onto data. +/// +/// NixStoreEntry represents a package in the Nix store (/nix/store) with its derivation +/// information and metadata. +/// +/// OpamPackage represents an OCaml package managed by the OPAM package manager with metadata +/// from .opam files. +/// +/// PEBinary represents metadata captured from a Portable Executable formatted binary (dll, +/// exe, etc.) +/// +/// PhpComposerInstalledEntry represents a single package entry from a composer v1/v2 +/// "installed.json" files (very similar to composer.lock files). +/// +/// PhpComposerLockEntry represents a single package entry found from a composer.lock file. +/// +/// PhpPearEntry represents a single package entry found within php pear metadata files. +/// +/// PhpPeclEntry represents a single package entry found within php pecl metadata files. +/// +/// PortageEntry represents a single package entry in the portage DB flat-file store. +/// +/// PythonPackage represents all captured data for a python egg or wheel package +/// (specifically as outlined in the PyPA core metadata specification +/// https://packaging.python.org/en/latest/specifications/core-metadata/). +/// +/// PythonPdmLockEntry represents a single package entry within a pdm.lock file. +/// +/// PythonRequirementsEntry represents a single entry within a [*-]requirements.txt file. +/// +/// PythonPipfileLockEntry represents a single package entry within a Pipfile.lock file. +/// +/// PythonPoetryLockEntry represents a single package entry within a Pipfile.lock file. +/// +/// PythonUvLockEntry represents a single package entry within a uv.lock file. +/// +/// RDescription represents metadata from an R package DESCRIPTION file containing package +/// information, dependencies, and author details. +/// +/// RpmArchive represents package metadata extracted directly from a .rpm archive file, +/// containing the same information as an RPM database entry. +/// +/// RpmDBEntry represents all captured data from a RPM DB package entry. +/// +/// RubyGemspec represents all metadata parsed from the *.gemspec file +/// +/// RustBinaryAuditEntry represents Rust crate metadata extracted from a compiled binary +/// using cargo-auditable format. +/// +/// RustCargoLockEntry represents a locked dependency from a Cargo.lock file with precise +/// version and checksum information. +/// +/// SnapEntry represents metadata for a Snap package extracted from snap.yaml or +/// snapcraft.yaml files. +/// +/// SwiftPackageManagerResolvedEntry represents a resolved dependency from a Package.resolved +/// file with its locked version and source location. +/// +/// SwiplPackEntry represents a SWI-Prolog package from the pack system with metadata about +/// the package and its dependencies. +/// +/// TerraformLockProviderEntry represents a single provider entry in a Terraform dependency +/// lock file (.terraform.lock.hcl). +/// +/// WordpressPluginEntry represents all metadata parsed from the wordpress plugin file +/// public partial class MetadataClass { + /// + /// Architecture is the target CPU architecture as defined in Arch architecture spec (e.g. + /// x86_64, aarch64, or "any" for arch-independent packages) + /// + /// Architecture is the target CPU architecture + /// + /// Architecture is the target architecture per Debian spec (specific arch like amd64/arm64, + /// wildcard like any, architecture-independent "all", or "source" for source packages) + /// + /// Architecture of the binary package (e.g. "amd64", "arm", etc.) + /// + /// Architecture is the target CPU architecture for the binary (extracted from GOARCH build + /// setting). + /// + /// Architecture is the target CPU architecture for build constraints (e.g., "amd64", + /// "arm64"). + /// + /// Arch is the target CPU architecture (e.g., "x86_64", "aarch64", "noarch"). + /// + /// Architecture is the target CPU architecture (e.g., "amd64", "arm64"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("architecture")] public string Architecture { get; set; } + + /// + /// Backup is the list of configuration files that pacman backs up before upgrades + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("backup")] public BackupElement[] Backup { get; set; } + + /// + /// BasePackage is the base package name this package was built from (source package in Arch + /// build system) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("basepackage")] public string Basepackage { get; set; } + + /// + /// Depends are the runtime dependencies required by this package + /// + /// Depends is the list of runtime dependencies with version constraints. + /// + /// Depends are the packages required for this package to function (will not be installed + /// unless these requirements are met, creates strict ordering constraint) + /// + /// Depends are the packages this package depends on + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("depends")] public string[] Depends { get; set; } + + /// + /// Description is a human-readable package description + /// + /// Description is a human-readable formula description + /// + /// Description is a human-readable module description + /// + /// Description is detailed package description + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] public string Description { get; set; } + + /// + /// Files are the files installed by this package + /// + /// Files are the file paths owned by this package (tracked via SPDX relationships) + /// + /// Files is the list of files installed by this package. + /// + /// Files are the list of files that are part of this JVM installation + /// + /// Files are the list of files under the nix/store path for this package + /// + /// Files are the files installed by this package (tracked in CONTENTS file) + /// + /// Files are the installed files listed in the RECORD file for wheels or installed-files.txt + /// for eggs. + /// + /// Files are the package files with their paths and hash digests + /// + /// Files are the file records for all files owned by this package. + /// + /// Files is logical list of files in the gem (NOT directly usable as filesystem paths. + /// Example: bundler gem lists "lib/bundler/vendor/uri/lib/uri/ldap.rb" but actual path is + /// "/usr/local/lib/ruby/3.2.0/bundler/vendor/uri/lib/uri/ldap.rb". Would need gem + /// installation path, ruby version, and env vars like GEM_HOME to resolve actual paths.) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("files")] public File[] Files { get; set; } + + /// + /// Package is the package name as found in the desc file + /// + /// Package is the package name as found in the installed file + /// + /// Package is the package name as found in the status file + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("package")] public string Package { get; set; } + + /// + /// Packager is the name and email of the person who packaged this (RFC822 format) + /// + /// Packager is packager name (if different from author) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("packager")] public string Packager { get; set; } + + /// + /// Provides are virtual packages provided by this package (allows other packages to depend + /// on capabilities rather than specific packages) + /// + /// Provides are virtual packages provided by this package (for capability-based + /// dependencies) + /// + /// Provides are the virtual packages provided by this package (allows other packages to + /// depend on capabilities. Can include versioned provides like "libdigest-md5-perl (= + /// 2.55.01)") + /// + /// Provides lists the virtual packages and capabilities this package provides. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("provides")] public string[] Provides { get; set; } + + /// + /// Reason is the installation reason tracked by pacman (0=explicitly installed by user, + /// 1=installed as dependency) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reason")] public long? Reason { get; set; } + + /// + /// Size is the installed size in bytes + /// + /// Size is the package archive size in bytes (.apk file size) + /// + /// Size is the package archive size in bytes. + /// + /// Size is the total installed size of the package in bytes. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("size")] public long? Size { get; set; } + + /// + /// URL is the upstream project URL + /// + /// URL is the full download URL for the package archive. + /// + /// URL is repository or project URL + /// + /// URL is the source download URL + /// + /// URL is download URL for the package source + /// + /// URL is the direct download URL or VCS URL if specified instead of a PyPI package. + /// + /// URL is the list of related URLs + /// + /// URL is the provider source address (e.g., "registry.terraform.io/hashicorp/aws"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("url")] public Checksum? Url { get; set; } + + /// + /// Validation is the validation method used for package integrity (e.g. pgp signature, + /// sha256 checksum) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("validation")] public string Validation { get; set; } + + /// + /// Version is the package version as found in the desc file + /// + /// Version is the package version as found in the installed file + /// + /// Version is the package version as found in the Bitnami SPDX file + /// + /// Version is the package version as found in the conda-meta JSON file. + /// + /// Version is the package version as found in the pubspec.lock file + /// + /// Version is the package version as found in the deps.json file + /// + /// Version is the package version as found in the packages.lock.json file + /// + /// Version is the binary package version as found in the status file + /// + /// Version is the package version as found in the mix.lock file + /// + /// Version is the package version as found in the rebar.lock file + /// + /// Version is the package version as found in package.json + /// + /// Version is kernel version string + /// + /// Version is module version string + /// + /// Version is the package version as found in the .rockspec file + /// + /// Version is the package version as found in the .opam file + /// + /// Version is the package version + /// + /// Version is the package version from the Version field in PKG-INFO or METADATA. + /// + /// Version is the upstream version of the package. + /// + /// Version is gem version as specified in the gemspec + /// + /// Version is crate version as specified in audit section of the build binary + /// + /// Version is crate version as specified in Cargo.toml + /// + /// Version is the package version as found in the .toml file + /// + /// Version is the locked provider version selected during terraform init. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("version")] public string Version { get; set; } + + /// + /// GitCommit is the git commit hash of the APK port definition in Alpine's aports repository + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("gitCommitOfApkPort")] public string GitCommitOfApkPort { get; set; } + + /// + /// InstalledSize is the total size of installed files in bytes + /// + /// InstalledSize is the total size of installed files in kilobytes + /// + /// InstalledSize is total size of installed files in bytes + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("installedSize")] public long? InstalledSize { get; set; } + + /// + /// Maintainer is the package maintainer name and email + /// + /// Maintainer is the package maintainer's name and email in RFC822 format (name must come + /// first, then email in angle brackets) + /// + /// Maintainer is current package maintainer + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("maintainer")] public string Maintainer { get; set; } + + /// + /// OriginPackage is the original source package name this binary was built from (used to + /// track which aport/source built this) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("originPackage")] public string OriginPackage { get; set; } + + /// + /// Checksum is the package content checksum for integrity verification + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pullChecksum")] public string PullChecksum { get; set; } + + /// + /// Dependencies are the runtime dependencies required by this package + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pullDependencies")] public string[] PullDependencies { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("matches")] public MatchElement[] Matches { get; set; } + + /// + /// Architecture is the target CPU architecture (amd64 or arm64 in Bitnami images) + /// + /// Arch is the target CPU architecture for the package (e.g., "arm64", "x86_64"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("arch")] public string Arch { get; set; } + + /// + /// Distro is the distribution name this package is for (base OS like debian, ubuntu, etc.) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("distro")] public string Distro { get; set; } + + /// + /// Name is the package name as found in the Bitnami SPDX file + /// + /// Name is the package name as found in the conda-meta JSON file. + /// + /// Name is the package name as found in the pubspec.lock file + /// + /// Name is the package name as found in the deps.json file + /// + /// Name is the package name as found in the packages.lock.json file + /// + /// Name is the package name as found in the mix.lock file + /// + /// Name is the package name as found in the rebar.lock file + /// + /// Name is the package name as found in package.json + /// + /// Name is kernel name (typically "Linux") + /// + /// Name is module name + /// + /// Name is the package name as found in the .rockspec file + /// + /// Name is the package name as found in the .opam file + /// + /// Name is package name in vendor/package format (e.g. symfony/console) + /// + /// Name is the package name + /// + /// Name is the package name from the Name field in PKG-INFO or METADATA. + /// + /// Name is the package name from the requirements file. + /// + /// Name is the RPM package name as found in the RPM database. + /// + /// Name is gem name as specified in the gemspec + /// + /// Name is crate name as specified in audit section of the build binary + /// + /// Name is crate name as specified in Cargo.toml + /// + /// Name is the package name as found in the .toml file + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("name")] public string Name { get; set; } + + /// + /// Path is the installation path in the filesystem where the package is located + /// + /// Path is the filesystem path to the package in Conan cache + /// + /// Path is the relative path to the package within the deps structure (e.g. + /// "app.metrics/3.0.0") + /// + /// Path is the filesystem path to the .ko kernel object file (absolute path) + /// + /// Path is full store path for this output (e.g. /nix/store/abc123...-package-1.0) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("path")] public string Path { get; set; } + + /// + /// Revision is the Bitnami-specific package revision number (incremented for Bitnami + /// rebuilds of same upstream version) + /// + /// Revision is git commit hash of the resolved package + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("revision")] public string Revision { get; set; } + + /// + /// Ref is the package reference string in format name/version@user/channel + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ref")] public string Ref { get; set; } - public string SyftOutpuPackageId { get; set; } + + /// + /// PackageID is a unique package variant identifier + /// + /// PackageID is a unique package variant identifier computed from settings/options (static + /// hash in Conan 1.x, can have collisions with complex dependency graphs) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("package_id")] + public string SyftOutputPackageId { get; set; } + + /// + /// BuildRequires are the build-time dependencies (e.g. cmake, compilers) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("build_requires")] public string[] BuildRequires { get; set; } + + /// + /// Context is the build context information + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("context")] public string Context { get; set; } + + /// + /// Options are package configuration options as key-value pairs (e.g. shared=True, fPIC=True) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("options")] public VersionResourceElement[] Options { get; set; } + + /// + /// Prev is the previous lock entry reference for versioning + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("prev")] public string Prev { get; set; } + + /// + /// PythonRequires are the Python dependencies needed for Conan recipes + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("py_requires")] public string[] PyRequires { get; set; } + + /// + /// Requires are the runtime package dependencies + /// + /// Requires lists the dependencies required by this package. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requires")] public string[] Requires { get; set; } + + /// + /// Channel is the Conan channel name indicating stability/purpose (e.g. stable, testing, + /// experimental) + /// + /// Channel is the Conda channel URL where the package was retrieved from. + /// + /// Channel is PEAR channel this package is from + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("channel")] public string Channel { get; set; } + + /// + /// PackageID is a unique package variant identifier (dynamic in Conan 2.0, more accurate + /// than V1) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("packageID")] public string PackageId { get; set; } + + /// + /// PackageRevision is a git-like revision hash of the built binary package + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("packageRevision")] public string PackageRevision { get; set; } + + /// + /// RecipeRevision is a git-like revision hash (RREV) of the recipe + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("recipeRevision")] public string RecipeRevision { get; set; } + + /// + /// TimeStamp is when this package was built/locked + /// + /// Timestamp is the Unix timestamp when the package was built. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("timestamp")] public Timestamp? Timestamp { get; set; } + + /// + /// Username is the Conan user/organization name + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("username")] public string Username { get; set; } + + /// + /// Checksum is the SHA-1 hash of the podspec file for integrity verification (generated via + /// `pod ipc spec ... | openssl sha1`), ensuring all team members use the same pod + /// specification version + /// + /// Checksums are the list of checksums for verification + /// + /// Checksum is content checksum for registry packages only (hexadecimal string). Cargo + /// doesn't require or include checksums for git dependencies. Used to detect MITM attacks by + /// verifying downloaded crate matches lockfile checksum. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("checksum")] public Checksum? Checksum { get; set; } + + /// + /// Build is the build string identifier (e.g., "h90dfc92_1014"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("build")] public string Build { get; set; } + + /// + /// BuildNumber is the sequential build number for this version. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("build_number")] public long? BuildNumber { get; set; } + + /// + /// ExtractedPackageDir is the local cache directory where the package was extracted. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("extracted_package_dir")] public string ExtractedPackageDir { get; set; } + + /// + /// Filename is the original package archive filename (e.g., + /// "zlib-1.2.11-h90dfc92_1014.tar.bz2"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("fn")] public string Fn { get; set; } + + /// + /// License is the package license identifier. + /// + /// License is module license (e.g. GPL, BSD) which must be compatible with kernel + /// + /// License is license identifier + /// + /// License is the list of license identifiers (SPDX format) + /// + /// License is the list of applicable licenses + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("license")] public Checksum? License { get; set; } + + /// + /// LicenseFamily is the general license category (e.g., "MIT", "Apache", "GPL"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("license_family")] public string LicenseFamily { get; set; } + + /// + /// Link contains installation source metadata from the link.json file. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("link")] public Link Link { get; set; } + + /// + /// MD5 is the MD5 hash of the package archive. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("md5")] public string Md5 { get; set; } + + /// + /// Noarch indicates if the package is platform-independent (e.g., "python", "generic"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("noarch")] public string Noarch { get; set; } + + /// + /// PathsData contains detailed file metadata from the paths.json file. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("paths_data")] public PathsData PathsData { get; set; } + + /// + /// SHA256 is the SHA-256 hash of the package archive. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sha256")] public string Sha256 { get; set; } + + /// + /// Subdir is the subdirectory within the channel (e.g., "osx-arm64", "linux-64"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("subdir")] public string Subdir { get; set; } + + /// + /// Documentation is the documentation site URL + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("documentation")] public string Documentation { get; set; } + + /// + /// Environment is SDK version constraints for Dart and Flutter + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("environment")] public Environment Environment { get; set; } + + /// + /// Homepage is the package homepage URL + /// + /// Homepage is the upstream project homepage URL + /// + /// Homepage is project homepage URL + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("homepage")] public string Homepage { get; set; } + + /// + /// IgnoredAdvisories are the security advisories to explicitly ignore for this package + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ignored_advisories")] public string[] IgnoredAdvisories { get; set; } + + /// + /// Platforms are the supported platforms (Android, iOS, web, etc.) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("platforms")] public string[] Platforms { get; set; } + + /// + /// PublishTo is the package repository to publish to, or "none" to prevent accidental + /// publishing + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("publish_to")] public string PublishTo { get; set; } + + /// + /// Repository is the source code repository URL + /// + /// Repository is CRAN or other repository name + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("repository")] public string Repository { get; set; } + + /// + /// HostedURL is the URL of the package repository for hosted packages (typically pub.dev, + /// but can be custom repository identified by hosted-url). When PUB_HOSTED_URL environment + /// variable changes, lockfile tracks the source. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("hosted_url")] public string HostedUrl { get; set; } + + /// + /// VcsURL is the URL of the VCS repository for git/path dependencies (for packages fetched + /// from version control systems like Git) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("vcs_url")] public string VcsUrl { get; set; } + + /// + /// Executables are the map of .NET Portable Executable files within this package with their + /// version resources + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("executables")] public Dictionary Executables { get; set; } + + /// + /// HashPath is the relative path to the .nupkg.sha512 hash file (e.g. + /// "app.metrics.3.0.0.nupkg.sha512") + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("hashPath")] public string HashPath { get; set; } + + /// + /// Sha512 is the SHA-512 hash of the NuGet package content WITHOUT the signed content for + /// verification (won't match hash from NuGet API or manual calculation of .nupkg file) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sha512")] public string Sha512 { get; set; } + + /// + /// ContentHash is the hash of the package content for verification + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("contentHash")] public string ContentHash { get; set; } + + /// + /// Type is the dependency type indicating how this dependency was added (Direct=explicit in + /// project file, Transitive=pulled in by another package, Project=project reference) + /// + /// Type is the type of the package (e.g. "rpm", "deb", "apk", etc.) + /// + /// Type is package type indicating purpose (library=reusable code, project=application, + /// metapackage=aggregates dependencies, etc.) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("type")] public string Type { get; set; } + + /// + /// AssemblyVersion is the .NET assembly version number (strong-named version) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("assemblyVersion")] public string AssemblyVersion { get; set; } + + /// + /// Comments are additional comments or description embedded in PE resources + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("comments")] public string Comments { get; set; } + + /// + /// CompanyName is the company that produced the file + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("companyName")] public string CompanyName { get; set; } + + /// + /// InternalName is the internal name of the file + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("internalName")] public string InternalName { get; set; } + + /// + /// LegalCopyright is the copyright notice string + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("legalCopyright")] public string LegalCopyright { get; set; } + + /// + /// ProductName is the name of the product this file is part of + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("productName")] public string ProductName { get; set; } + + /// + /// ProductVersion is the version of the product (may differ from AssemblyVersion) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("productVersion")] public string ProductVersion { get; set; } + + /// + /// PreDepends are the packages that must be installed and configured BEFORE even starting + /// installation of this package (stronger than Depends, discouraged unless absolutely + /// necessary as it adds strict constraints for apt) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("preDepends")] public string[] PreDepends { get; set; } + + /// + /// Source is the source package name this binary was built from (one source can produce + /// multiple binary packages) + /// + /// Source is the source repository information for development (typically git repo, used + /// when passing --prefer-source). Originates from source code repository. + /// + /// Source is the source registry or repository where this crate came from + /// + /// Source is the source registry or repository URL in format + /// "registry+https://github.com/rust-lang/crates.io-index" for registry packages + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("source")] public SourceUnion? Source { get; set; } + + /// + /// SourceVersion is the source package version (may differ from binary version when binNMU + /// rebuilds occur) + /// + /// SourceVersion is the source code version identifier + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sourceVersion")] public string SourceVersion { get; set; } + + /// + /// Commit is the commit hash of the source repository for which the binary was built from + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("commit")] public string Commit { get; set; } + + /// + /// OS is the OS name, typically corresponding to ID in os-release (e.g. "fedora") + /// + /// OperatingSystem is the target OS for build constraints (e.g., "linux", "darwin", + /// "windows"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("os")] public string Os { get; set; } + + /// + /// OSCPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. + /// cpe:/o:fedoraproject:fedora:33) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("osCPE")] public string OsCpe { get; set; } + + /// + /// osVersion is the version of the OS, typically corresponding to VERSION_ID in os-release + /// (e.g. "33") + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("osVersion")] public string OsVersion { get; set; } + + /// + /// SourceRepo is the URL to the source repository for which the binary was built from + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sourceRepo")] public string SourceRepo { get; set; } + + /// + /// System is a context-specific name for the system that the binary package is intended to + /// run on or a part of + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("system")] public string System { get; set; } + + /// + /// Vendor is the individual or organization that produced the source code for the binary + /// + /// Vendor is the organization that packaged the software. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("vendor")] public string Vendor { get; set; } + + /// + /// PkgHash is the outer checksum (SHA-256) of the entire Hex package tarball for integrity + /// verification (preferred method, replaces deprecated inner checksum) + /// + /// PkgHash is the outer checksum (SHA-256) of the entire Hex package tarball for integrity + /// verification (preferred method over deprecated inner checksum) + /// + /// PkgHash is the package content hash for verification + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pkgHash")] public string PkgHash { get; set; } + + /// + /// PkgHashExt is the extended package hash format (inner checksum is deprecated - SHA-256 of + /// concatenated file contents excluding CHECKSUM file, now replaced by outer checksum) + /// + /// PkgHashExt is the extended package hash format (inner checksum deprecated - was SHA-256 + /// of concatenated file contents) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pkgHashExt")] public string PkgHashExt { get; set; } + + /// + /// Comment is the inline comment associated with this uses statement + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("comment")] public string Comment { get; set; } + + /// + /// Value is the action reference (e.g. "actions/checkout@v3") + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("value")] public string Value { get; set; } + + /// + /// BuildSettings contains the Go build settings and flags used to compile the binary (e.g., + /// GOARCH, GOOS, CGO_ENABLED). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("goBuildSettings")] public VersionResourceElement[] GoBuildSettings { get; set; } + + /// + /// GoCompiledVersion is the version of Go used to compile the binary. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("goCompiledVersion")] public string GoCompiledVersion { get; set; } + + /// + /// GoCryptoSettings contains FIPS and cryptographic configuration settings if present. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("goCryptoSettings")] public string[] GoCryptoSettings { get; set; } + + /// + /// GoExperiments lists experimental Go features enabled during compilation (e.g., "arenas", + /// "cgocheck2"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("goExperiments")] public string[] GoExperiments { get; set; } + + /// + /// H1Digest is the Go module hash in h1: format for the main module from go.sum. + /// + /// H1Digest is the Go module hash in h1: format from go.sum for verifying module contents. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("h1Digest")] public string H1Digest { get; set; } + + /// + /// MainModule is the main module path for the binary (e.g., "github.com/anchore/syft"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mainModule")] public string MainModule { get; set; } + + /// + /// BuildTags are the build tags used to conditionally compile code (e.g., + /// "integration,debug"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("buildTags")] public string BuildTags { get; set; } + + /// + /// CgoEnabled indicates whether CGO was enabled for this package. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cgoEnabled")] public bool? CgoEnabled { get; set; } + + /// + /// SnapshotURL is the URL to the Stack snapshot this package came from + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("snapshotURL")] public string SnapshotUrl { get; set; } + + /// + /// Tap is Homebrew tap this formula belongs to (e.g. "homebrew/core") + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tap")] public string Tap { get; set; } + + /// + /// ArchiveDigests is cryptographic hashes of the archive file + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("digest")] public DigestElement[] Digest { get; set; } + + /// + /// Manifest is parsed META-INF/MANIFEST.MF contents + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("manifest")] public Manifest Manifest { get; set; } + + /// + /// PomProject is parsed pom.xml file contents + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pomProject")] public PomProject PomProject { get; set; } + + /// + /// PomProperties is parsed pom.properties file contents + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pomProperties")] public PomProperties PomProperties { get; set; } + + /// + /// VirtualPath is path within the archive hierarchy, where nested entries are delimited with + /// ':' (for nested JARs) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("virtualPath")] public string VirtualPath { get; set; } + + /// + /// Release is JVM release information and version details + /// + /// Release is the package release number or distribution-specific version suffix. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("release")] public ReleaseUnion? Release { get; set; } + + /// + /// Author is package author name + /// + /// Author is who built the kernel + /// + /// Author is module author name and email + /// + /// Author is the package author name from the Author field. + /// + /// Author is package author(s) + /// + /// Author is author name + /// + /// Author is plugin author name + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("author")] public string Author { get; set; } + + /// + /// Private is whether this is a private package + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("private")] public bool? Private { get; set; } + + /// + /// Integrity is Subresource Integrity hash for verification using standard SRI format + /// (sha512-... or sha1-...). npm changed from SHA-1 to SHA-512 in newer versions. For + /// registry sources this is the integrity from registry, for remote tarballs it's SHA-512 of + /// the file. npm verifies tarball matches this hash before unpacking, throwing EINTEGRITY + /// error if mismatch detected. + /// + /// Integrity is Subresource Integrity hash for verification (SRI format) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("integrity")] public string Integrity { get; set; } + + /// + /// Resolved is URL where this package was downloaded from (registry source) + /// + /// Resolved is URL where this package was downloaded from + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resolved")] public string Resolved { get; set; } + + /// + /// BuildTime is when the kernel was built + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("buildTime")] public string BuildTime { get; set; } + + /// + /// ExtendedVersion is additional version information + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("extendedVersion")] public string ExtendedVersion { get; set; } + + /// + /// Format is kernel image format (e.g. bzImage, zImage) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("format")] public string Format { get; set; } + + /// + /// RootDevice is root device number + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("rootDevice")] public long? RootDevice { get; set; } + + /// + /// RWRootFS is whether root filesystem is mounted read-write + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("rwRootFS")] public bool? RwRootFs { get; set; } + + /// + /// SwapDevice is swap device number + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("swapDevice")] public long? SwapDevice { get; set; } + + /// + /// VideoMode is default video mode setting + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("videoMode")] public string VideoMode { get; set; } + + /// + /// KernelVersion is kernel version this module was built for + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("kernelVersion")] public string KernelVersion { get; set; } + + /// + /// Parameters are the module parameters that can be configured at load time (user-settable + /// values like module options) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("parameters")] public Dictionary Parameters { get; set; } + + /// + /// VersionMagic is version magic string for compatibility checking (includes kernel version, + /// SMP status, module loading capabilities like "3.17.4-302.fc21.x86_64 SMP mod_unload + /// modversions"). Module will NOT load if vermagic doesn't match running kernel. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("versionMagic")] public string VersionMagic { get; set; } + + /// + /// Dependencies are the map of dependency names to version constraints + /// + /// Dependencies are the list of required dependencies + /// + /// Dependencies are the dependency specifications, without environment qualifiers + /// + /// Dependencies are the package's runtime dependencies with version constraints. + /// + /// Dependencies are the list of dependencies with version constraints + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("dependencies")] public Dependencies? Dependencies { get; set; } + + /// + /// Kb is Knowledge Base article number (e.g. "5001028") + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("kb")] public string Kb { get; set; } + + /// + /// ProductID is MSRC Product ID (e.g. "Windows 10 Version 1703 for 32-bit Systems") + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("product_id")] public string ProductId { get; set; } + + /// + /// Derivation is information about the .drv file that describes how this package was built + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("derivation")] public Derivation Derivation { get; set; } + + /// + /// Output is the specific output name for multi-output packages (empty string for default + /// "out" output, can be "bin", "dev", "doc", etc.) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("output")] public string Output { get; set; } + + /// + /// OutputHash is hash prefix of the store path basename (first part before the dash) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("outputHash")] public string OutputHash { get; set; } + + /// + /// Licenses are the list of applicable licenses + /// + /// Licenses is license string which may be an expression (e.g. "GPL-2 OR Apache-2.0") + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("licenses")] public Checksum? Licenses { get; set; } + + /// + /// VersionResources contains key-value pairs extracted from the PE file's version resource + /// section (e.g., FileVersion, ProductName, CompanyName). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("VersionResources")] public VersionResourceElement[] VersionResources { get; set; } + + /// + /// Authors are the list of package authors with name/email/homepage + /// + /// Authors are the list of gem authors (stored as array regardless of using `author` or + /// `authors` method in gemspec) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("authors")] public Author[] Authors { get; set; } + + /// + /// Bin is the list of binary/executable files that should be added to PATH + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("bin")] public string[] Bin { get; set; } + + /// + /// Dist is distribution archive information for production (typically zip/tar, default + /// install method). Packaged version of released code. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("dist")] public Dist Dist { get; set; } + + /// + /// Keywords are the list of keywords for package discovery/search + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("keywords")] public string[] Keywords { get; set; } + + /// + /// NotificationURL is the URL to notify when package is installed (for tracking/statistics) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("notification-url")] public string NotificationUrl { get; set; } + + /// + /// Provide is virtual packages/functionality provided by this package (allows other packages + /// to depend on capabilities) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("provide")] public Dictionary Provide { get; set; } + + /// + /// Require is runtime dependencies with version constraints (package will not install unless + /// these requirements can be met) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("require")] public Dictionary Require { get; set; } + + /// + /// RequireDev is development-only dependencies (not installed in production, only when + /// developing this package or running tests) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("require-dev")] public Dictionary RequireDev { get; set; } + + /// + /// Suggest is optional but recommended dependencies (suggestions for packages that would + /// extend functionality) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("suggest")] public Dictionary Suggest { get; set; } + + /// + /// Time is timestamp when this package version was released + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("time")] public string Time { get; set; } + + /// + /// AuthorEmail is the package author's email address from the Author-Email field. + /// + /// AuthorEmail is author email address + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("authorEmail")] public string AuthorEmail { get; set; } + + /// + /// DirectURLOrigin contains VCS or direct URL installation information from direct_url.json. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("directUrlOrigin")] public DirectUrlOrigin DirectUrlOrigin { get; set; } + + /// + /// Platform indicates the target platform for the package (e.g., "any", "linux", "win32"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("platform")] public string Platform { get; set; } + + /// + /// ProvidesExtra lists optional feature names that can be installed via extras (e.g., "dev", + /// "test"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providesExtra")] public string[] ProvidesExtra { get; set; } + + /// + /// RequiresDist lists the package dependencies with version specifiers from Requires-Dist + /// fields. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requiresDist")] public string[] RequiresDist { get; set; } + + /// + /// RequiresPython specifies the Python version requirement (e.g., ">=3.6"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requiresPython")] public string RequiresPython { get; set; } + + /// + /// SitePackagesRootPath is the root directory path containing the package (e.g., + /// "/usr/lib/python3.9/site-packages"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sitePackagesRootPath")] public string SitePackagesRootPath { get; set; } + + /// + /// TopLevelPackages are the top-level Python module names from top_level.txt file. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("topLevelPackages")] public string[] TopLevelPackages { get; set; } + + /// + /// Summary provides a description of the package + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("summary")] public string Summary { get; set; } + + /// + /// Extras are the optional features to install from the package (e.g., package[dev,test]). + /// + /// Extras are optional feature groups that include additional dependencies. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("extras")] public Extra[] Extras { get; set; } + + /// + /// Markers are environment marker expressions for conditional installation (e.g., + /// "python_version >= '3.8'"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("markers")] public string Markers { get; set; } + + /// + /// VersionConstraint specifies version requirements. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("versionConstraint")] public string VersionConstraint { get; set; } + + /// + /// Hashes are the package file hash values in the format "algorithm:digest" for integrity + /// verification. + /// + /// Hashes are cryptographic checksums for the provider plugin archives across different + /// platforms. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("hashes")] public string[] Hashes { get; set; } + + /// + /// Index is the PyPI index name where the package should be fetched from. + /// + /// Index is the package repository name where the package should be fetched from. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("index")] public string Index { get; set; } + + /// + /// Built is R version and platform this was built with + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("built")] public string Built { get; set; } + + /// + /// Imports are the packages imported in the NAMESPACE + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("imports")] public string[] Imports { get; set; } + + /// + /// NeedsCompilation is whether this package requires compilation + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("needsCompilation")] public bool? NeedsCompilation { get; set; } + + /// + /// Suggests are the optional packages that extend functionality + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("suggests")] public string[] Suggests { get; set; } + + /// + /// Title is short one-line package title + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("title")] public string Title { get; set; } + [JsonPropertyName("epoch")] public long? Epoch { get; set; } + + /// + /// ModularityLabel identifies the module stream for modular RPM packages (e.g., + /// "nodejs:12:20200101"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("modularityLabel")] public string ModularityLabel { get; set; } + + /// + /// Signatures contains GPG signature metadata for package verification. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("signatures")] public SignatureElement[] Signatures { get; set; } + + /// + /// SourceRpm is the source RPM filename that was used to build this package. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sourceRpm")] public string SourceRpm { get; set; } + + /// + /// Base is the base snap name that this snap depends on (e.g., "core20", "core22"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("base")] public string Base { get; set; } + + /// + /// SnapName is the snap package name. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("snapName")] public string SnapName { get; set; } + + /// + /// SnapType indicates the snap type (base, kernel, app, gadget, or snapd). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("snapType")] public string SnapType { get; set; } + + /// + /// SnapVersion is the snap package version. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("snapVersion")] public string SnapVersion { get; set; } + + /// + /// PackagerEmail is packager email address + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("packagerEmail")] public string PackagerEmail { get; set; } + + /// + /// Constraints specifies the version constraints for the provider (e.g., "~> 4.0"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("constraints")] public string Constraints { get; set; } + + /// + /// AuthorURI is author's website URL + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("authorUri")] public string AuthorUri { get; set; } + + /// + /// PluginInstallDirectory is directory name where the plugin is installed + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pluginInstallDirectory")] public string PluginInstallDirectory { get; set; } } +/// +/// PhpComposerAuthors represents author information for a PHP Composer package from the +/// authors field in composer.json. +/// public partial class AuthorClass { + /// + /// Email is author's email address + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("email")] public string Email { get; set; } + + /// + /// Homepage is author's personal or company website + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("homepage")] public string Homepage { get; set; } + + /// + /// Name is author's full name + /// + [JsonPropertyName("name")] public string Name { get; set; } } public partial class BackupElement { + /// + /// Digests contains file content hashes for integrity verification + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("digest")] public DigestElement[] Digest { get; set; } + + /// + /// GID is the file owner group ID as recorded by pacman + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("gid")] public string Gid { get; set; } + + /// + /// Link is the symlink target path if this is a symlink + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("link")] public string Link { get; set; } + + /// + /// Path is the file path relative to the filesystem root + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("path")] public string Path { get; set; } + + /// + /// Size is the file size in bytes + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("size")] public string Size { get; set; } + + /// + /// Time is the file modification timestamp + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("time")] public DateTimeOffset? Time { get; set; } + + /// + /// Type is the file type (e.g. regular file, directory, symlink) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("type")] public string Type { get; set; } + + /// + /// UID is the file owner user ID as recorded by pacman + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("uid")] public string Uid { get; set; } } +/// +/// Digest represents a cryptographic hash of file contents. +/// +/// Digest is the file content hash for integrity verification +/// +/// Digest is the file content hash (typically MD5 for dpkg compatibility with legacy +/// systems) +/// +/// Digest is file content hash (MD5 for regular files in CONTENTS format: "obj filename +/// md5hash mtime") +/// +/// Digest contains the hash algorithm and value for file integrity verification. +/// public partial class DigestElement { + /// + /// Algorithm specifies the hash algorithm used (e.g., "sha256", "md5"). + /// + [JsonPropertyName("algorithm")] public string Algorithm { get; set; } + + /// + /// Value is the hexadecimal string representation of the hash. + /// + [JsonPropertyName("value")] public string Value { get; set; } } +/// +/// PythonPoetryLockDependencyEntry represents a single dependency entry within a Poetry lock +/// file. +/// +/// PythonUvLockDependencyEntry represents a single dependency entry within a uv lock file. +/// public partial class DependencyClass { + /// + /// Extras are the optional feature names from the dependency that should be installed. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("extras")] public string[] Extras { get; set; } + + /// + /// Markers are environment marker expressions that conditionally enable the dependency + /// (e.g., "python_version >= '3.8'"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("markers")] public string Markers { get; set; } + + /// + /// Name is the dependency package name. + /// + [JsonPropertyName("name")] public string Name { get; set; } + + /// + /// Optional indicates whether this dependency is optional (only needed for certain extras). + /// + [JsonPropertyName("optional")] public bool Optional { get; set; } + + /// + /// Version is the locked version or version constraint for the dependency. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("version")] public string Version { get; set; } } +/// +/// Derivation is information about the .drv file that describes how this package was built +/// +/// NixDerivation represents a Nix .drv file that describes how to build a package including +/// inputs, outputs, and build instructions. +/// public partial class Derivation { + /// + /// InputDerivations are the list of other derivations that were inputs to this build + /// (dependencies) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("inputDerivations")] public InputDerivationElement[] InputDerivations { get; set; } + + /// + /// InputSources are the list of source file paths that were inputs to this build + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("inputSources")] public string[] InputSources { get; set; } + + /// + /// Path is path to the .drv file in Nix store + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("path")] public string Path { get; set; } + + /// + /// System is target system string indicating where derivation can be built (e.g. + /// "x86_64-linux", "aarch64-darwin"). Must match current system for local builds. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("system")] public string System { get; set; } } +/// +/// NixDerivationReference represents a reference to another derivation used as a build input +/// or runtime dependency. +/// public partial class InputDerivationElement { + /// + /// Outputs are which outputs of the referenced derivation were used (e.g. ["out"], ["bin", + /// "dev"]) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("outputs")] public string[] Outputs { get; set; } + + /// + /// Path is path to the referenced .drv file + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("path")] public string Path { get; set; } } +/// +/// DirectURLOrigin contains VCS or direct URL installation information from +/// direct_url.json. +/// +/// PythonDirectURLOriginInfo represents installation source metadata from direct_url.json +/// for packages installed from VCS or direct URLs. +/// public partial class DirectUrlOrigin { + /// + /// CommitID is the VCS commit hash if installed from version control. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("commitId")] public string CommitId { get; set; } + + /// + /// URL is the source URL from which the package was installed. + /// + [JsonPropertyName("url")] public string Url { get; set; } + + /// + /// VCS is the version control system type (e.g., "git", "hg"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("vcs")] public string Vcs { get; set; } } +/// +/// Dist is distribution archive information for production (typically zip/tar, default +/// install method). Packaged version of released code. +/// +/// PhpComposerExternalReference represents source or distribution information for a PHP +/// package, indicating where the package code is retrieved from. +/// +/// Source is the source repository information for development (typically git repo, used +/// when passing --prefer-source). Originates from source code repository. +/// public partial class Dist { + /// + /// Reference is git commit hash or version tag for source, or archive version for dist + /// + [JsonPropertyName("reference")] public string Reference { get; set; } + + /// + /// Shasum is SHA hash of the archive file for integrity verification (dist only) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("shasum")] public string Shasum { get; set; } + + /// + /// Type is reference type (git for source VCS, zip/tar for dist archives) + /// + [JsonPropertyName("type")] public string Type { get; set; } + + /// + /// URL is the URL to the resource (git repository URL or archive download URL) + /// + [JsonPropertyName("url")] public string Url { get; set; } } +/// +/// Environment is SDK version constraints for Dart and Flutter +/// +/// DartPubspecEnvironment represents SDK version constraints from the environment section of +/// pubspec.yaml. +/// public partial class Environment { + /// + /// Flutter is the Flutter SDK version constraint if this is a Flutter package + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("flutter")] public string Flutter { get; set; } + + /// + /// SDK is the Dart SDK version constraint + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sdk")] public string Sdk { get; set; } } +/// +/// DotnetPortableExecutableEntry is a struct that represents a single entry found within +/// "VersionResources" section of a .NET Portable Executable binary file. +/// public partial class ExecutableValue { + /// + /// AssemblyVersion is the .NET assembly version number (strong-named version) + /// + [JsonPropertyName("assemblyVersion")] public string AssemblyVersion { get; set; } + + /// + /// Comments are additional comments or description embedded in PE resources + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("comments")] public string Comments { get; set; } + + /// + /// CompanyName is the company that produced the file + /// + [JsonPropertyName("companyName")] public string CompanyName { get; set; } + + /// + /// InternalName is the internal name of the file + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("internalName")] public string InternalName { get; set; } + + /// + /// LegalCopyright is the copyright notice string + /// + [JsonPropertyName("legalCopyright")] public string LegalCopyright { get; set; } + + /// + /// ProductName is the name of the product this file is part of + /// + [JsonPropertyName("productName")] public string ProductName { get; set; } + + /// + /// ProductVersion is the version of the product (may differ from AssemblyVersion) + /// + [JsonPropertyName("productVersion")] public string ProductVersion { get; set; } } +/// +/// PythonPoetryLockExtraEntry represents an optional feature group in a Poetry lock file. +/// +/// PythonUvLockExtraEntry represents an optional feature group in a uv lock file. +/// public partial class ExtraClass { + /// + /// Dependencies are the package names required when this extra is installed. + /// + [JsonPropertyName("dependencies")] public string[] Dependencies { get; set; } + + /// + /// Name is the optional feature name (e.g., "dev", "test"). + /// + [JsonPropertyName("name")] public string Name { get; set; } } +/// +/// ApkFileRecord represents a single file listing and metadata from a APK DB entry (which +/// may have many of these file records). +/// +/// DpkgFileRecord represents a single file attributed to a debian package. +/// +/// PortageFileRecord represents a single file attributed to a portage package. +/// +/// PythonFileRecord represents a single entry within a RECORD file for a python wheel or egg +/// package +/// +/// RpmFileRecord represents the file metadata for a single file attributed to a RPM package. +/// public partial class FileFile { + /// + /// Digests contains file content hashes for integrity verification + /// + /// Digest is the file content hash for integrity verification + /// + /// Digest is the file content hash (typically MD5 for dpkg compatibility with legacy + /// systems) + /// + /// Digest is file content hash (MD5 for regular files in CONTENTS format: "obj filename + /// md5hash mtime") + /// + /// Digest contains the hash algorithm and value for file integrity verification. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("digest")] public Digest? Digest { get; set; } + + /// + /// GID is the file owner group ID as recorded by pacman + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("gid")] public string Gid { get; set; } + + /// + /// Link is the symlink target path if this is a symlink + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("link")] public string Link { get; set; } + + /// + /// Path is the file path relative to the filesystem root + /// + /// Path is the installed file path from the RECORD file. + /// + /// Path is the absolute file path where the file is installed. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("path")] public string Path { get; set; } + + /// + /// Size is the file size in bytes + /// + /// Size is the file size in bytes as a string. + /// + /// Size is the file size in bytes. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("size")] public Timestamp? Size { get; set; } + + /// + /// Time is the file modification timestamp + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("time")] public DateTimeOffset? Time { get; set; } + + /// + /// Type is the file type (e.g. regular file, directory, symlink) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("type")] public string Type { get; set; } + + /// + /// UID is the file owner user ID as recorded by pacman + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("uid")] public string Uid { get; set; } + + /// + /// OwnerGID is the file owner group ID + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ownerGid")] public string OwnerGid { get; set; } + + /// + /// OwnerUID is the file owner user ID + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ownerUid")] public string OwnerUid { get; set; } + + /// + /// Permissions is the file permission mode string (e.g. "0755", "0644") + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("permissions")] public string Permissions { get; set; } + + /// + /// IsConfigFile is whether this file is marked as a configuration file (dpkg will preserve + /// user modifications during upgrades) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("isConfigFile")] public bool? IsConfigFile { get; set; } + + /// + /// Flags indicates the file type (e.g., "%config", "%doc", "%ghost"). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("flags")] public string Flags { get; set; } + + /// + /// GroupName is the group name for the file. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("groupName")] public string GroupName { get; set; } + + /// + /// Mode is the file permission mode bits following Unix stat.h conventions. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mode")] public long? Mode { get; set; } + + /// + /// UserName is the owner username for the file. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("userName")] public string UserName { get; set; } } -public partial class PurpleSyftOutpu +/// +/// Digest represents a cryptographic hash of file contents. +/// +/// Digest is the file content hash for integrity verification +/// +/// Digest is the file content hash (typically MD5 for dpkg compatibility with legacy +/// systems) +/// +/// Digest is file content hash (MD5 for regular files in CONTENTS format: "obj filename +/// md5hash mtime") +/// +/// Digest contains the hash algorithm and value for file integrity verification. +/// +/// PythonFileDigest represents the file metadata for a single file attributed to a python +/// package. +/// +public partial class PurpleSyftOutput { + /// + /// Algorithm specifies the hash algorithm used (e.g., "sha256", "md5"). + /// + /// Algorithm is the hash algorithm used (e.g., "sha256"). + /// + [JsonPropertyName("algorithm")] public string Algorithm { get; set; } + + /// + /// Value is the hexadecimal string representation of the hash. + /// + /// Value is the hex-encoded hash digest value. + /// + [JsonPropertyName("value")] public string Value { get; set; } } +/// +/// Options are package configuration options as key-value pairs (e.g. shared=True, +/// fPIC=True) +/// +/// KeyValues represents an ordered collection of key-value pairs that preserves insertion +/// order. +/// +/// KeyValue represents a single key-value pair. +/// public partial class VersionResourceElement { + /// + /// Key is the key name + /// + [JsonPropertyName("key")] public string Key { get; set; } + + /// + /// Value is the value associated with the key + /// + [JsonPropertyName("value")] public string Value { get; set; } } +/// +/// Link contains installation source metadata from the link.json file. +/// +/// CondaLink represents link metadata from a Conda package's link.json file describing +/// package installation source. +/// public partial class Link { + /// + /// Source is the original path where the package was extracted from cache. + /// + [JsonPropertyName("source")] public string Source { get; set; } + + /// + /// Type indicates the link type (1 for hard link, 2 for soft link, 3 for copy). + /// + [JsonPropertyName("type")] public long Type { get; set; } } +/// +/// Manifest is parsed META-INF/MANIFEST.MF contents +/// +/// JavaManifest represents the fields of interest extracted from a Java archive's +/// META-INF/MANIFEST.MF file. +/// public partial class Manifest { + /// + /// Main is main manifest attributes as key-value pairs + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("main")] public VersionResourceElement[] Main { get; set; } + + /// + /// Sections are the named sections from the manifest (e.g. per-entry attributes) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sections")] public VersionResourceElement[][] Sections { get; set; } } +/// +/// ClassifierMatch represents a single matched value within a binary file and the "class" +/// name the search pattern represents. +/// public partial class MatchElement { + [JsonPropertyName("classifier")] public string Classifier { get; set; } + + [JsonPropertyName("location")] public LocationElement Location { get; set; } } +/// +/// LinuxKernelModuleParameter represents a configurable parameter for a kernel module with +/// its type and description. +/// public partial class ParameterValue { + /// + /// Description is a human-readable parameter description explaining what the parameter + /// controls + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] public string Description { get; set; } + + /// + /// Type is parameter data type (e.g. int, string, bool, array types) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("type")] public string Type { get; set; } } +/// +/// PathsData contains detailed file metadata from the paths.json file. +/// +/// CondaPathsData represents the paths.json file structure from a Conda package containing +/// file metadata. +/// public partial class PathsData { + /// + /// Paths is the list of file metadata entries for all files in the package. + /// + [JsonPropertyName("paths")] public PathElement[] Paths { get; set; } + + /// + /// PathsVersion is the schema version of the paths data format. + /// + [JsonPropertyName("paths_version")] public long PathsVersion { get; set; } } +/// +/// CondaPathData represents metadata for a single file within a Conda package from the +/// paths.json file. +/// public partial class PathElement { + /// + /// Path is the file path relative to the Conda environment root. + /// + [JsonPropertyName("_path")] public string Path { get; set; } + + /// + /// PathType indicates the link type for the file (e.g., "hardlink", "softlink", "directory"). + /// + [JsonPropertyName("path_type")] public string PathType { get; set; } + + /// + /// SHA256 is the SHA-256 hash of the file contents. + /// + [JsonPropertyName("sha256")] public string Sha256 { get; set; } + + /// + /// SHA256InPrefix is the SHA-256 hash of the file after prefix replacement during + /// installation. + /// + [JsonPropertyName("sha256_in_prefix")] public string Sha256InPrefix { get; set; } + + /// + /// SizeInBytes is the file size in bytes. + /// + [JsonPropertyName("size_in_bytes")] public long SizeInBytes { get; set; } } +/// +/// PomProject is parsed pom.xml file contents +/// +/// JavaPomProject represents fields of interest extracted from a Java archive's pom.xml file. +/// public partial class PomProject { + /// + /// ArtifactID is Maven artifact identifier (project name) + /// + [JsonPropertyName("artifactId")] public string ArtifactId { get; set; } + + /// + /// Description is detailed project description + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("description")] public string Description { get; set; } + + /// + /// GroupID is Maven group identifier (reversed domain name like org.apache.maven) + /// + [JsonPropertyName("groupId")] public string GroupId { get; set; } + + /// + /// Name is a human-readable project name (displayed in Maven-generated documentation) + /// + [JsonPropertyName("name")] public string Name { get; set; } + + /// + /// Parent is the parent POM reference for inheritance (child POMs inherit configuration from + /// parent) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("parent")] public Parent Parent { get; set; } + + /// + /// Path is path to the pom.xml file within the archive + /// + [JsonPropertyName("path")] public string Path { get; set; } + + /// + /// URL is the project URL (typically project website or repository) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("url")] public string Url { get; set; } + + /// + /// Version is project version (together with groupId and artifactId forms Maven coordinates + /// groupId:artifactId:version) + /// + [JsonPropertyName("version")] public string Version { get; set; } } +/// +/// Parent is the parent POM reference for inheritance (child POMs inherit configuration from +/// parent) +/// +/// JavaPomParent contains the fields within the parent tag in a pom.xml file +/// public partial class Parent { + /// + /// ArtifactID is the parent Maven artifact identifier + /// + [JsonPropertyName("artifactId")] public string ArtifactId { get; set; } + + /// + /// GroupID is the parent Maven group identifier + /// + [JsonPropertyName("groupId")] public string GroupId { get; set; } + + /// + /// Version is the parent version (child inherits configuration from this specific version of + /// parent POM) + /// + [JsonPropertyName("version")] public string Version { get; set; } } +/// +/// PomProperties is parsed pom.properties file contents +/// +/// JavaPomProperties represents the fields of interest extracted from a Java archive's +/// pom.properties file. +/// public partial class PomProperties { + /// + /// ArtifactID is Maven artifact identifier, the name of the jar/artifact (unique within the + /// groupId scope) + /// + [JsonPropertyName("artifactId")] public string ArtifactId { get; set; } + + /// + /// Extra is additional custom properties not in standard Maven coordinates + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("extraFields")] public Dictionary ExtraFields { get; set; } + + /// + /// GroupID is Maven group identifier uniquely identifying the project across all projects + /// (follows reversed domain name convention like com.company.project) + /// + [JsonPropertyName("groupId")] public string GroupId { get; set; } + + /// + /// Name is the project name + /// + [JsonPropertyName("name")] public string Name { get; set; } + + /// + /// Path is path to the pom.properties file within the archive + /// + [JsonPropertyName("path")] public string Path { get; set; } + + /// + /// Scope is dependency scope determining when dependency is available (compile=default all + /// phases, test=test compilation/execution only, runtime=runtime and test not compile, + /// provided=expected from JDK or container) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("scope")] public string Scope { get; set; } + + /// + /// Version is artifact version + /// + [JsonPropertyName("version")] public string Version { get; set; } } +/// +/// Release is JVM release information and version details +/// +/// JavaVMRelease represents JVM version and build information extracted from the release +/// file in a Java installation. +/// public partial class ReleaseClass { + /// + /// BuildInfo contains additional build information + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("buildInfo")] public string BuildInfo { get; set; } + + /// + /// BuildSource Git SHA of the build repository + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("buildSource")] public string BuildSource { get; set; } + + /// + /// BuildSourceRepo refers to rhe repository URL for the build source + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("buildSourceRepo")] public string BuildSourceRepo { get; set; } + + /// + /// BuildType can be 'commercial' (used in some older oracle JDK distributions) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("buildType")] public string BuildType { get; set; } + + /// + /// FullVersion is extracted from the 'java.runtime.version' JVM property + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("fullVersion")] public string FullVersion { get; set; } + + /// + /// ImageType can be 'JDK' or 'JRE' + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("imageType")] public string ImageType { get; set; } + + /// + /// Implementor is extracted with the `java.vendor` JVM property + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("implementor")] public string Implementor { get; set; } + + /// + /// ImplementorVersion is extracted with the `java.vendor.version` JVM property + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("implementorVersion")] public string ImplementorVersion { get; set; } + + /// + /// JavaRuntimeVersion is extracted from the 'java.runtime.version' JVM property + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("javaRuntimeVersion")] public string JavaRuntimeVersion { get; set; } + + /// + /// JavaVersion matches that from `java -version` command output + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("javaVersion")] public string JavaVersion { get; set; } + + /// + /// JavaVersionDate is extracted from the 'java.version.date' JVM property + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("javaVersionDate")] public string JavaVersionDate { get; set; } + + /// + /// JvmVariant specifies the JVM variant (e.g., Hotspot or OpenJ9) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("jvmVariant")] public string JvmVariant { get; set; } + + /// + /// JvmVersion is extracted from the 'java.vm.version' JVM property + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("jvmVersion")] public string JvmVersion { get; set; } + + /// + /// Libc can either be 'glibc' or 'musl' + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("libc")] public string Libc { get; set; } + + /// + /// Modules is a list of JVM modules that are packaged + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("modules")] public string[] Modules { get; set; } + + /// + /// OsArch is the target CPU architecture + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("osArch")] public string OsArch { get; set; } + + /// + /// OsName is the name of the target runtime operating system environment + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("osName")] public string OsName { get; set; } + + /// + /// OsVersion is the version of the target runtime operating system environment + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("osVersion")] public string OsVersion { get; set; } + + /// + /// SemanticVersion is derived from the OpenJDK version + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("semanticVersion")] public string SemanticVersion { get; set; } + + /// + /// Source refers to the origin repository of OpenJDK source + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("source")] public string Source { get; set; } + + /// + /// SourceRepo refers to the OpenJDK repository URL + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sourceRepo")] public string SourceRepo { get; set; } } +/// +/// RpmSignature represents a GPG signature for an RPM package used for authenticity +/// verification. +/// public partial class SignatureElement { + /// + /// PublicKeyAlgorithm is the public key algorithm used for signing (e.g., "RSA"). + /// + [JsonPropertyName("algo")] public string Algo { get; set; } + + /// + /// Created is the timestamp when the signature was created. + /// + [JsonPropertyName("created")] public string Created { get; set; } + + /// + /// HashAlgorithm is the hash algorithm used for the signature (e.g., "SHA256"). + /// + [JsonPropertyName("hash")] public string Hash { get; set; } + + /// + /// IssuerKeyID is the GPG key ID that created the signature. + /// + [JsonPropertyName("issuer")] public string Issuer { get; set; } } +/// +/// Descriptor describes what created the document as well as surrounding metadata +/// public partial class Descriptor { + [JsonPropertyName("configuration")] public object Configuration { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("version")] public string Version { get; set; } } public partial class Distro { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("bugReportURL")] public string BugReportUrl { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("buildID")] public string BuildId { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cpeName")] public string CpeName { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("extendedSupport")] public bool? ExtendedSupport { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("homeURL")] public string HomeUrl { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("id")] public string Id { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("idLike")] public string[] IdLike { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("imageID")] public string ImageId { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("imageVersion")] public string ImageVersion { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("prettyName")] public string PrettyName { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("privacyPolicyURL")] public string PrivacyPolicyUrl { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("supportEnd")] public string SupportEnd { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("supportURL")] public string SupportUrl { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("variant")] public string Variant { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("variantID")] public string VariantId { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("version")] public string Version { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("versionCodename")] public string VersionCodename { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("versionID")] public string VersionId { get; set; } } public partial class FileElement { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("contents")] public string Contents { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("digests")] public DigestElement[] Digests { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("executable")] public Executable Executable { get; set; } + + [JsonPropertyName("id")] public string Id { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("licenses")] public FileLicense[] Licenses { get; set; } + + [JsonPropertyName("location")] public Location Location { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("metadata")] public Metadata Metadata { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("unknowns")] public string[] Unknowns { get; set; } } +/// +/// Executable contains metadata about binary files and their security features. +/// public partial class Executable { + /// + /// ELFSecurityFeatures contains ELF-specific security hardening information when Format is + /// ELF. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("elfSecurityFeatures")] public ElfSecurityFeatures ElfSecurityFeatures { get; set; } + + /// + /// Format denotes either ELF, Mach-O, or PE + /// + [JsonPropertyName("format")] public string Format { get; set; } + + /// + /// HasEntrypoint indicates whether the binary has an entry point function. + /// + [JsonPropertyName("hasEntrypoint")] public bool HasEntrypoint { get; set; } + + /// + /// HasExports indicates whether the binary exports symbols. + /// + [JsonPropertyName("hasExports")] public bool HasExports { get; set; } + + /// + /// ImportedLibraries lists the shared libraries required by this executable. + /// + [JsonPropertyName("importedLibraries")] public string[] ImportedLibraries { get; set; } } +/// +/// ELFSecurityFeatures contains ELF-specific security hardening information when Format is +/// ELF. +/// +/// ELFSecurityFeatures captures security hardening and protection mechanisms in ELF binaries. +/// public partial class ElfSecurityFeatures { + /// + /// ControlFlowIntegrity represents runtime checks to ensure a program's control flow adheres + /// to the legal paths determined at compile time, thus protecting against various types of + /// control-flow hijacking attacks + /// see https://clang.llvm.org/docs/ControlFlowIntegrity.html + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cfi")] public bool? Cfi { get; set; } + + /// + /// DynamicSharedObject indicates whether the binary is a shared library. + /// + [JsonPropertyName("dso")] public bool Dso { get; set; } + + /// + /// ClangFortifySource is a broad suite of extensions to libc aimed at catching misuses of + /// common library functions + /// see + /// https://android.googlesource.com/platform//bionic/+/d192dbecf0b2a371eb127c0871f77a9caf81c4d2/docs/clang_fortify_anatomy.md + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("fortify")] public bool? Fortify { get; set; } + + /// + /// NoExecutable indicates whether NX (no-execute) protection is enabled for the stack. + /// + [JsonPropertyName("nx")] public bool Nx { get; set; } + + /// + /// PositionIndependentExecutable indicates whether the binary is compiled as PIE. + /// + [JsonPropertyName("pie")] public bool Pie { get; set; } + + /// + /// RelocationReadOnly indicates the RELRO protection level. + /// + [JsonPropertyName("relRO")] public string RelRo { get; set; } + + /// + /// LlvmSafeStack represents a compiler-based security mechanism that separates the stack + /// into a safe stack for storing return addresses and other critical data, and an unsafe + /// stack for everything else, to mitigate stack-based memory corruption errors + /// see https://clang.llvm.org/docs/SafeStack.html + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("safeStack")] public bool? SafeStack { get; set; } + + /// + /// StackCanary indicates whether stack smashing protection is enabled. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("stackCanary")] public bool? StackCanary { get; set; } + + /// + /// SymbolTableStripped indicates whether debugging symbols have been removed. + /// + [JsonPropertyName("symbolTableStripped")] public bool SymbolTableStripped { get; set; } } public partial class FileLicense { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("evidence")] public Evidence Evidence { get; set; } + + [JsonPropertyName("spdxExpression")] public string SpdxExpression { get; set; } + + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("value")] public string Value { get; set; } } public partial class Evidence { + [JsonPropertyName("confidence")] public long Confidence { get; set; } + + [JsonPropertyName("extent")] public long Extent { get; set; } + + [JsonPropertyName("offset")] public long Offset { get; set; } } +/// +/// Coordinates contains the minimal information needed to describe how to find a file within +/// any possible source object (e.g. +/// public partial class Location { + /// + /// FileSystemID is an ID representing and entire filesystem. For container images, this is a + /// layer digest. For directories or a root filesystem, this is blank. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("layerID")] public string LayerId { get; set; } + + /// + /// RealPath is the canonical absolute form of the path accessed (all symbolic links have + /// been followed and relative path components like '.' and '..' have been removed). + /// + [JsonPropertyName("path")] public string Path { get; set; } } public partial class Metadata { + [JsonPropertyName("groupID")] public long GroupId { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("linkDestination")] public string LinkDestination { get; set; } + + [JsonPropertyName("mimeType")] public string MimeType { get; set; } + + [JsonPropertyName("mode")] public long Mode { get; set; } + + [JsonPropertyName("size")] public long Size { get; set; } + + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("userID")] public long UserId { get; set; } } public partial class Schema { + [JsonPropertyName("url")] public string Url { get; set; } + + [JsonPropertyName("version")] public string Version { get; set; } } +/// +/// Instead, the Supplier can be determined by the user of syft and passed as a config or +/// flag to help fulfill the NTIA minimum elements. +/// public partial class SourceClass { + [JsonPropertyName("id")] public string Id { get; set; } + + [JsonPropertyName("metadata")] public object Metadata { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("supplier")] public string Supplier { get; set; } + + [JsonPropertyName("type")] public string Type { get; set; } + + [JsonPropertyName("version")] public string Version { get; set; } } @@ -622,10 +3297,10 @@ public partial struct Extra public partial struct Digest { public DigestElement[] DigestElementArray; - public PurpleSyftOutpu PurpleSyftOutpu; + public PurpleSyftOutput PurpleSyftOutput; public static implicit operator Digest(DigestElement[] DigestElementArray) => new Digest { DigestElementArray = DigestElementArray }; - public static implicit operator Digest(PurpleSyftOutpu PurpleSyftOutpu) => new Digest { PurpleSyftOutpu = PurpleSyftOutpu }; + public static implicit operator Digest(PurpleSyftOutput PurpleSyftOutput) => new Digest { PurpleSyftOutput = PurpleSyftOutput }; } public partial struct Timestamp @@ -663,3 +3338,509 @@ public partial struct SourceUnion public static implicit operator SourceUnion(Dist Dist) => new SourceUnion { Dist = Dist }; public static implicit operator SourceUnion(string String) => new SourceUnion { String = String }; } + +public partial class SyftOutput +{ + public static SyftOutput FromJson(string json) => JsonSerializer.Deserialize(json, Microsoft.ComponentDetection.Detectors.Linux.Contracts.Converter.Settings); +} + +public static class Serialize +{ + public static string ToJson(this SyftOutput self) => JsonSerializer.Serialize(self, Microsoft.ComponentDetection.Detectors.Linux.Contracts.Converter.Settings); +} + +internal static class Converter +{ + public static readonly JsonSerializerOptions Settings = new(JsonSerializerDefaults.General) + { + Converters = + { + AuthorConverter.Singleton, + ChecksumConverter.Singleton, + DependenciesConverter.Singleton, + DependencyConverter.Singleton, + ExtraConverter.Singleton, + FileConverter.Singleton, + DigestConverter.Singleton, + TimestampConverter.Singleton, + ReleaseUnionConverter.Singleton, + SourceUnionConverter.Singleton, + new DateOnlyConverter(), + new TimeOnlyConverter(), + IsoDateTimeOffsetConverter.Singleton + }, + }; +} + +internal class AuthorConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(Author); + + public override Author Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringValue = reader.GetString(); + return new Author { String = stringValue }; + case JsonTokenType.StartObject: + var objectValue = JsonSerializer.Deserialize(ref reader, options); + return new Author { AuthorClass = objectValue }; + } + throw new Exception("Cannot unmarshal type Author"); + } + + public override void Write(Utf8JsonWriter writer, Author value, JsonSerializerOptions options) + { + if (value.String != null) + { + JsonSerializer.Serialize(writer, value.String, options); + return; + } + if (value.AuthorClass != null) + { + JsonSerializer.Serialize(writer, value.AuthorClass, options); + return; + } + throw new Exception("Cannot marshal type Author"); + } + + public static readonly AuthorConverter Singleton = new AuthorConverter(); +} + +internal class ChecksumConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(Checksum); + + public override Checksum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringValue = reader.GetString(); + return new Checksum { String = stringValue }; + case JsonTokenType.StartArray: + var arrayValue = JsonSerializer.Deserialize(ref reader, options); + return new Checksum { StringArray = arrayValue }; + } + throw new Exception("Cannot unmarshal type Checksum"); + } + + public override void Write(Utf8JsonWriter writer, Checksum value, JsonSerializerOptions options) + { + if (value.String != null) + { + JsonSerializer.Serialize(writer, value.String, options); + return; + } + if (value.StringArray != null) + { + JsonSerializer.Serialize(writer, value.StringArray, options); + return; + } + throw new Exception("Cannot marshal type Checksum"); + } + + public static readonly ChecksumConverter Singleton = new ChecksumConverter(); +} + +internal class DependenciesConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(Dependencies); + + public override Dependencies Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.StartObject: + var objectValue = JsonSerializer.Deserialize>(ref reader, options); + return new Dependencies { StringMap = objectValue }; + case JsonTokenType.StartArray: + var arrayValue = JsonSerializer.Deserialize(ref reader, options); + return new Dependencies { AnythingArray = arrayValue }; + } + throw new Exception("Cannot unmarshal type Dependencies"); + } + + public override void Write(Utf8JsonWriter writer, Dependencies value, JsonSerializerOptions options) + { + if (value.AnythingArray != null) + { + JsonSerializer.Serialize(writer, value.AnythingArray, options); + return; + } + if (value.StringMap != null) + { + JsonSerializer.Serialize(writer, value.StringMap, options); + return; + } + throw new Exception("Cannot marshal type Dependencies"); + } + + public static readonly DependenciesConverter Singleton = new DependenciesConverter(); +} + +internal class DependencyConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(Dependency); + + public override Dependency Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringValue = reader.GetString(); + return new Dependency { String = stringValue }; + case JsonTokenType.StartObject: + var objectValue = JsonSerializer.Deserialize(ref reader, options); + return new Dependency { DependencyClass = objectValue }; + } + throw new Exception("Cannot unmarshal type Dependency"); + } + + public override void Write(Utf8JsonWriter writer, Dependency value, JsonSerializerOptions options) + { + if (value.String != null) + { + JsonSerializer.Serialize(writer, value.String, options); + return; + } + if (value.DependencyClass != null) + { + JsonSerializer.Serialize(writer, value.DependencyClass, options); + return; + } + throw new Exception("Cannot marshal type Dependency"); + } + + public static readonly DependencyConverter Singleton = new DependencyConverter(); +} + +internal class ExtraConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(Extra); + + public override Extra Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringValue = reader.GetString(); + return new Extra { String = stringValue }; + case JsonTokenType.StartObject: + var objectValue = JsonSerializer.Deserialize(ref reader, options); + return new Extra { ExtraClass = objectValue }; + } + throw new Exception("Cannot unmarshal type Extra"); + } + + public override void Write(Utf8JsonWriter writer, Extra value, JsonSerializerOptions options) + { + if (value.String != null) + { + JsonSerializer.Serialize(writer, value.String, options); + return; + } + if (value.ExtraClass != null) + { + JsonSerializer.Serialize(writer, value.ExtraClass, options); + return; + } + throw new Exception("Cannot marshal type Extra"); + } + + public static readonly ExtraConverter Singleton = new ExtraConverter(); +} + +internal class FileConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(File); + + public override File Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringValue = reader.GetString(); + return new File { String = stringValue }; + case JsonTokenType.StartObject: + var objectValue = JsonSerializer.Deserialize(ref reader, options); + return new File { FileFile = objectValue }; + } + throw new Exception("Cannot unmarshal type File"); + } + + public override void Write(Utf8JsonWriter writer, File value, JsonSerializerOptions options) + { + if (value.String != null) + { + JsonSerializer.Serialize(writer, value.String, options); + return; + } + if (value.FileFile != null) + { + JsonSerializer.Serialize(writer, value.FileFile, options); + return; + } + throw new Exception("Cannot marshal type File"); + } + + public static readonly FileConverter Singleton = new FileConverter(); +} + +internal class DigestConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(Digest); + + public override Digest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.StartObject: + var objectValue = JsonSerializer.Deserialize(ref reader, options); + return new Digest { PurpleSyftOutput = objectValue }; + case JsonTokenType.StartArray: + var arrayValue = JsonSerializer.Deserialize(ref reader, options); + return new Digest { DigestElementArray = arrayValue }; + } + throw new Exception("Cannot unmarshal type Digest"); + } + + public override void Write(Utf8JsonWriter writer, Digest value, JsonSerializerOptions options) + { + if (value.DigestElementArray != null) + { + JsonSerializer.Serialize(writer, value.DigestElementArray, options); + return; + } + if (value.PurpleSyftOutput != null) + { + JsonSerializer.Serialize(writer, value.PurpleSyftOutput, options); + return; + } + throw new Exception("Cannot marshal type Digest"); + } + + public static readonly DigestConverter Singleton = new DigestConverter(); +} + +internal class TimestampConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(Timestamp); + + public override Timestamp Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.Number: + var integerValue = reader.GetInt64(); + return new Timestamp { Integer = integerValue }; + case JsonTokenType.String: + var stringValue = reader.GetString(); + return new Timestamp { String = stringValue }; + } + throw new Exception("Cannot unmarshal type Timestamp"); + } + + public override void Write(Utf8JsonWriter writer, Timestamp value, JsonSerializerOptions options) + { + if (value.Integer != null) + { + JsonSerializer.Serialize(writer, value.Integer.Value, options); + return; + } + if (value.String != null) + { + JsonSerializer.Serialize(writer, value.String, options); + return; + } + throw new Exception("Cannot marshal type Timestamp"); + } + + public static readonly TimestampConverter Singleton = new TimestampConverter(); +} + +internal class ReleaseUnionConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(ReleaseUnion); + + public override ReleaseUnion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringValue = reader.GetString(); + return new ReleaseUnion { String = stringValue }; + case JsonTokenType.StartObject: + var objectValue = JsonSerializer.Deserialize(ref reader, options); + return new ReleaseUnion { ReleaseClass = objectValue }; + } + throw new Exception("Cannot unmarshal type ReleaseUnion"); + } + + public override void Write(Utf8JsonWriter writer, ReleaseUnion value, JsonSerializerOptions options) + { + if (value.String != null) + { + JsonSerializer.Serialize(writer, value.String, options); + return; + } + if (value.ReleaseClass != null) + { + JsonSerializer.Serialize(writer, value.ReleaseClass, options); + return; + } + throw new Exception("Cannot marshal type ReleaseUnion"); + } + + public static readonly ReleaseUnionConverter Singleton = new ReleaseUnionConverter(); +} + +internal class SourceUnionConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(SourceUnion); + + public override SourceUnion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringValue = reader.GetString(); + return new SourceUnion { String = stringValue }; + case JsonTokenType.StartObject: + var objectValue = JsonSerializer.Deserialize(ref reader, options); + return new SourceUnion { Dist = objectValue }; + } + throw new Exception("Cannot unmarshal type SourceUnion"); + } + + public override void Write(Utf8JsonWriter writer, SourceUnion value, JsonSerializerOptions options) + { + if (value.String != null) + { + JsonSerializer.Serialize(writer, value.String, options); + return; + } + if (value.Dist != null) + { + JsonSerializer.Serialize(writer, value.Dist, options); + return; + } + throw new Exception("Cannot marshal type SourceUnion"); + } + + public static readonly SourceUnionConverter Singleton = new SourceUnionConverter(); +} + +public class DateOnlyConverter : JsonConverter +{ + private readonly string serializationFormat; + public DateOnlyConverter() : this(null) { } + + public DateOnlyConverter(string? serializationFormat) + { + this.serializationFormat = serializationFormat ?? "yyyy-MM-dd"; + } + + public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var value = reader.GetString(); + return DateOnly.Parse(value!); + } + + public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString(serializationFormat)); +} + +public class TimeOnlyConverter : JsonConverter +{ + private readonly string serializationFormat; + + public TimeOnlyConverter() : this(null) { } + + public TimeOnlyConverter(string? serializationFormat) + { + this.serializationFormat = serializationFormat ?? "HH:mm:ss.fff"; + } + + public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var value = reader.GetString(); + return TimeOnly.Parse(value!); + } + + public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString(serializationFormat)); +} + +internal class IsoDateTimeOffsetConverter : JsonConverter +{ + public override bool CanConvert(Type t) => t == typeof(DateTimeOffset); + + private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"; + + private DateTimeStyles _dateTimeStyles = DateTimeStyles.RoundtripKind; + private string? _dateTimeFormat; + private CultureInfo? _culture; + + public DateTimeStyles DateTimeStyles + { + get => _dateTimeStyles; + set => _dateTimeStyles = value; + } + + public string? DateTimeFormat + { + get => _dateTimeFormat ?? string.Empty; + set => _dateTimeFormat = (string.IsNullOrEmpty(value)) ? null : value; + } + + public CultureInfo Culture + { + get => _culture ?? CultureInfo.CurrentCulture; + set => _culture = value; + } + + public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) + { + string text; + + + if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal + || (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal) + { + value = value.ToUniversalTime(); + } + + text = value.ToString(_dateTimeFormat ?? DefaultDateTimeFormat, Culture); + + writer.WriteStringValue(text); + } + + public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + string? dateText = reader.GetString(); + + if (string.IsNullOrEmpty(dateText) == false) + { + if (!string.IsNullOrEmpty(_dateTimeFormat)) + { + return DateTimeOffset.ParseExact(dateText, _dateTimeFormat, Culture, _dateTimeStyles); + } + else + { + return DateTimeOffset.Parse(dateText, Culture, _dateTimeStyles); + } + } + else + { + return default(DateTimeOffset); + } + } + + + public static readonly IsoDateTimeOffsetConverter Singleton = new IsoDateTimeOffsetConverter(); +} +#pragma warning restore CS8618 +#pragma warning restore CS8601 +#pragma warning restore CS8603 diff --git a/src/Microsoft.ComponentDetection.Detectors/linux/LinuxScanner.cs b/src/Microsoft.ComponentDetection.Detectors/linux/LinuxScanner.cs index e83340149..38817916c 100644 --- a/src/Microsoft.ComponentDetection.Detectors/linux/LinuxScanner.cs +++ b/src/Microsoft.ComponentDetection.Detectors/linux/LinuxScanner.cs @@ -4,6 +4,7 @@ namespace Microsoft.ComponentDetection.Detectors.Linux; using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.ComponentDetection.Common.Telemetry.Records; @@ -14,7 +15,6 @@ namespace Microsoft.ComponentDetection.Detectors.Linux; using Microsoft.ComponentDetection.Detectors.Linux.Factories; using Microsoft.ComponentDetection.Detectors.Linux.Filters; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; /// /// Scanner for Linux container layers using Syft. @@ -129,7 +129,7 @@ public async Task> ScanLinuxAsync( } catch (Exception e) { - syftTelemetryRecord.Exception = JsonConvert.SerializeObject(e); + syftTelemetryRecord.Exception = JsonSerializer.Serialize(e); this.logger.LogError(e, "Failed to run syft"); throw; } @@ -167,7 +167,7 @@ public async Task> ScanLinuxAsync( try { - var syftOutput = JsonConvert.DeserializeObject(stdout); + var syftOutput = SyftOutput.FromJson(stdout); // Apply artifact filters (e.g., Mariner 2.0 workaround) var validArtifacts = syftOutput.Artifacts.AsEnumerable(); @@ -230,7 +230,7 @@ public async Task> ScanLinuxAsync( }); // Track detected components in telemetry - syftTelemetryRecord.Components = JsonConvert.SerializeObject( + syftTelemetryRecord.Components = JsonSerializer.Serialize( componentsWithLayers.Select(c => c.Component.Id) ); diff --git a/test/Microsoft.ComponentDetection.Detectors.Tests/LinuxScannerTests.cs b/test/Microsoft.ComponentDetection.Detectors.Tests/LinuxScannerTests.cs index 230018a5d..eaa2c2bb4 100644 --- a/test/Microsoft.ComponentDetection.Detectors.Tests/LinuxScannerTests.cs +++ b/test/Microsoft.ComponentDetection.Detectors.Tests/LinuxScannerTests.cs @@ -21,198 +21,203 @@ namespace Microsoft.ComponentDetection.Detectors.Tests; [TestCategory("Governance/ComponentDetection")] public class LinuxScannerTests { - private const string SyftOutputLicensesFieldAndAuthor = - @"{ - ""distro"": { - ""id"":""test-distribution"", - ""versionId"":""1.0.0"" - }, - ""artifacts"": [ - { - ""name"":""test"", - ""version"":""1.0.0"", - ""type"":""deb"", - ""locations"": [ - { - ""path"": ""/var/lib/dpkg/status"", - ""layerID"": ""sha256:f95fc50d21d981f1efe1f04109c2c3287c271794f5d9e4fdf9888851a174a971"" - } - ], - ""metadata"": { - ""author"": ""John Doe"" + private const string SyftOutputLicensesFieldAndAuthor = """ + { + "distro": { + "id":"test-distribution", + "versionID":"1.0.0" + }, + "artifacts": [ + { + "name":"test", + "version":"1.0.0", + "type":"deb", + "locations": [ + { + "path": "/var/lib/dpkg/status", + "layerID": "sha256:f95fc50d21d981f1efe1f04109c2c3287c271794f5d9e4fdf9888851a174a971" + } + ], + "metadata": { + "author": "John Doe" + }, + "licenses": [ + { + "value": "MIT" }, - ""licenses"": [ - { - ""value"": ""MIT"", - }, - { - ""value"": ""GPLv2"", - }, - { - ""value"": ""GPLv3"", - } - ] - } - ] - }"; + { + "value": "GPLv2" + }, + { + "value": "GPLv3" + } + ] + } + ] + } + """; - private const string SyftOutputLicenseFieldAndMaintainer = - @"{ - ""distro"": { - ""id"":""test-distribution"", - ""versionId"":""1.0.0"" - }, - ""artifacts"": [ - { - ""name"":""test"", - ""version"":""1.0.0"", - ""type"":""deb"", - ""locations"": [ - { - ""path"": ""/var/lib/dpkg/status"", - ""layerID"": ""sha256:f95fc50d21d981f1efe1f04109c2c3287c271794f5d9e4fdf9888851a174a971"" - } - ], - ""metadata"": { - ""maintainer"": ""John Doe"", - ""license"": ""MIT, GPLv2, GPLv3"" + private const string SyftOutputLicenseFieldAndMaintainer = """ + { + "distro": { + "id":"test-distribution", + "versionID":"1.0.0" + }, + "artifacts": [ + { + "name":"test", + "version":"1.0.0", + "type":"deb", + "locations": [ + { + "path": "/var/lib/dpkg/status", + "layerID": "sha256:f95fc50d21d981f1efe1f04109c2c3287c271794f5d9e4fdf9888851a174a971" } + ], + "metadata": { + "maintainer": "John Doe", + "license": "MIT, GPLv2, GPLv3" } - ] - }"; + } + ] + } + """; + + private const string SyftOutputNoAuthorOrLicense = """ + { + "distro": { + "id":"test-distribution", + "versionID":"1.0.0" + }, + "artifacts": [ + { + "name":"test", + "version":"1.0.0", + "type":"deb", + "locations": [ + { + "path": "/var/lib/dpkg/status", + "layerID": "sha256:f95fc50d21d981f1efe1f04109c2c3287c271794f5d9e4fdf9888851a174a971" + } + ] + } + ] + } + """; - private const string SyftOutputNoAuthorOrLicense = - @"{ - ""distro"": { - ""id"":""test-distribution"", - ""versionId"":""1.0.0"" + private const string SyftOutputIgnoreInvalidMarinerPackages = """ + { + "distro": { + "prettyName": "CBL-Mariner/Linux", + "name": "Common Base Linux Mariner", + "id": "mariner", + "version": "2.0.20250304", + "versionID": "2.0" + }, + "artifacts": [ + { + "id": "4af20256df269904", + "name": "busybox", + "version": "1.35.0", + "type": "rpm", + "foundBy": "elf-binary-package-cataloger", + "locations": [ + { + "path": "/usr/sbin/busybox", + "layerID": "sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6", + "accessPath": "/usr/sbin/busybox", + "annotations": { "evidence": "primary" } + } + ], + "cpes": [ + { + "cpe": "cpe:2.3:a:busybox:busybox:1.35.0:*:*:*:*:*:*:*", + "source": "syft-generated" + } + ], + "purl": "pkg:rpm/mariner/busybox@1.35.0?distro=mariner-2.0", + "metadataType": "elf-binary-package-note-json-payload", + "metadata": { "type": "rpm", "os": "mariner", "osVersion": "2.0" } }, - ""artifacts"": [ - { - ""name"":""test"", - ""version"":""1.0.0"", - ""type"":""deb"", - ""locations"": [ - { - ""path"": ""/var/lib/dpkg/status"", - ""layerID"": ""sha256:f95fc50d21d981f1efe1f04109c2c3287c271794f5d9e4fdf9888851a174a971"" - } - ], + { + "id": "45849b2d67d236b0", + "name": "busybox", + "version": "1.35.0-13.cm2", + "type": "rpm", + "foundBy": "rpm-db-cataloger", + "locations": [ + { + "path": "/var/lib/rpmmanifest/container-manifest-2", + "layerID": "sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6", + "accessPath": "/var/lib/rpmmanifest/container-manifest-2", + "annotations": { "evidence": "primary" } + } + ], + "cpes": [ + { + "cpe": "cpe:2.3:a:microsoftcorporation:busybox:1.35.0-13.cm2:*:*:*:*:*:*:*", + "source": "syft-generated" + }, + { + "cpe": "cpe:2.3:a:busybox:busybox:1.35.0-13.cm2:*:*:*:*:*:*:*", + "source": "syft-generated" + } + ], + "purl": "pkg:rpm/busybox@1.35.0-13.cm2?arch=x86_64&upstream=busybox-1.35.0-13.cm2.src.rpm", + "metadataType": "rpm-db-entry", + "metadata": { + "name": "busybox", + "version": "1.35.0", + "epoch": null, + "architecture": "x86_64", + "release": "13.cm2", + "sourceRpm": "busybox-1.35.0-13.cm2.src.rpm", + "size": 3512551, + "vendor": "Microsoft Corporation", + "files": null } - ] - }"; - - private const string SyftOutputIgnoreInvalidMarinerPackages = - @"{ - ""distro"": { - ""prettyName"": ""CBL-Mariner/Linux"", - ""name"": ""Common Base Linux Mariner"", - ""id"": ""mariner"", - ""version"": ""2.0.20250304"", - ""versionID"": ""2.0"", - }, - ""artifacts"": [ - { - ""id"": ""4af20256df269904"", - ""name"": ""busybox"", - ""version"": ""1.35.0"", - ""type"": ""rpm"", - ""foundBy"": ""elf-binary-package-cataloger"", - ""locations"": [ - { - ""path"": ""/usr/sbin/busybox"", - ""layerID"": ""sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6"", - ""accessPath"": ""/usr/sbin/busybox"", - ""annotations"": { ""evidence"": ""primary"" } - } - ], - ""cpes"": [ - { - ""cpe"": ""cpe:2.3:a:busybox:busybox:1.35.0:*:*:*:*:*:*:*"", - ""source"": ""syft-generated"" - } - ], - ""purl"": ""pkg:rpm/mariner/busybox@1.35.0?distro=mariner-2.0"", - ""metadataType"": ""elf-binary-package-note-json-payload"", - ""metadata"": { ""type"": ""rpm"", ""os"": ""mariner"", ""osVersion"": ""2.0"" } - }, - { - ""id"": ""45849b2d67d236b0"", - ""name"": ""busybox"", - ""version"": ""1.35.0-13.cm2"", - ""type"": ""rpm"", - ""foundBy"": ""rpm-db-cataloger"", - ""locations"": [ - { - ""path"": ""/var/lib/rpmmanifest/container-manifest-2"", - ""layerID"": ""sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6"", - ""accessPath"": ""/var/lib/rpmmanifest/container-manifest-2"", - ""annotations"": { ""evidence"": ""primary"" } - } - ], - ""cpes"": [ - { - ""cpe"": ""cpe:2.3:a:microsoftcorporation:busybox:1.35.0-13.cm2:*:*:*:*:*:*:*"", - ""source"": ""syft-generated"" - }, - { - ""cpe"": ""cpe:2.3:a:busybox:busybox:1.35.0-13.cm2:*:*:*:*:*:*:*"", - ""source"": ""syft-generated"" - } - ], - ""purl"": ""pkg:rpm/busybox@1.35.0-13.cm2?arch=x86_64&upstream=busybox-1.35.0-13.cm2.src.rpm"", - ""metadataType"": ""rpm-db-entry"", - ""metadata"": { - ""name"": ""busybox"", - ""version"": ""1.35.0"", - ""epoch"": null, - ""architecture"": ""x86_64"", - ""release"": ""13.cm2"", - ""sourceRpm"": ""busybox-1.35.0-13.cm2.src.rpm"", - ""size"": 3512551, - ""vendor"": ""Microsoft Corporation"", - ""files"": null + } + ] + } + """; + + private const string SyftOutputRemoveNonduplicatedMarinerPackages = """ + { + "distro": { + "prettyName": "CBL-Mariner/Linux", + "name": "Common Base Linux Mariner", + "id": "mariner", + "version": "2.0.20250304", + "versionID": "2.0" + }, + "artifacts": [ + { + "id": "4af20256df269904", + "name": "busybox", + "version": "1.35.0", + "type": "rpm", + "foundBy": "elf-binary-package-cataloger", + "locations": [ + { + "path": "/usr/sbin/busybox", + "layerID": "sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6", + "accessPath": "/usr/sbin/busybox", + "annotations": { "evidence": "primary" } } - }, - ] - }"; - - private const string SyftOutputRemoveNonduplicatedMarinerPackages = - @"{ - ""distro"": { - ""prettyName"": ""CBL-Mariner/Linux"", - ""name"": ""Common Base Linux Mariner"", - ""id"": ""mariner"", - ""version"": ""2.0.20250304"", - ""versionID"": ""2.0"", - }, - ""artifacts"": [ - { - ""id"": ""4af20256df269904"", - ""name"": ""busybox"", - ""version"": ""1.35.0"", - ""type"": ""rpm"", - ""foundBy"": ""elf-binary-package-cataloger"", - ""locations"": [ - { - ""path"": ""/usr/sbin/busybox"", - ""layerID"": ""sha256:81caca2c07d9859b258a9cdfb1b1ab9d063f30ab73a4de9ea2ae760fd175bac6"", - ""accessPath"": ""/usr/sbin/busybox"", - ""annotations"": { ""evidence"": ""primary"" } - } - ], - ""cpes"": [ - { - ""cpe"": ""cpe:2.3:a:busybox:busybox:1.35.0:*:*:*:*:*:*:*"", - ""source"": ""syft-generated"" - } - ], - ""purl"": ""pkg:rpm/mariner/busybox@1.35.0?distro=mariner-2.0"", - ""metadataType"": ""elf-binary-package-note-json-payload"", - ""metadata"": { ""type"": ""rpm"", ""os"": ""mariner"", ""osVersion"": ""2.0"" } - }, - ] - }"; + ], + "cpes": [ + { + "cpe": "cpe:2.3:a:busybox:busybox:1.35.0:*:*:*:*:*:*:*", + "source": "syft-generated" + } + ], + "purl": "pkg:rpm/mariner/busybox@1.35.0?distro=mariner-2.0", + "metadataType": "elf-binary-package-note-json-payload", + "metadata": { "type": "rpm", "os": "mariner", "osVersion": "2.0" } + } + ] + } + """; private readonly LinuxScanner linuxScanner; private readonly Mock mockDockerService; @@ -440,59 +445,60 @@ await this.linuxScanner.ScanLinuxAsync( [TestMethod] public async Task TestLinuxScanner_SupportsMultipleComponentTypes_Async() { - const string syftOutputWithMixedTypes = - @"{ - ""distro"": { - ""id"":""ubuntu"", - ""versionId"":""22.04"" + const string syftOutputWithMixedTypes = """ + { + "distro": { + "id":"ubuntu", + "versionID":"22.04" }, - ""artifacts"": [ + "artifacts": [ { - ""name"":""curl"", - ""version"":""7.81.0-1ubuntu1.10"", - ""type"":""deb"", - ""locations"": [ + "name":"curl", + "version":"7.81.0-1ubuntu1.10", + "type":"deb", + "locations": [ { - ""path"": ""/var/lib/dpkg/status"", - ""layerID"": ""sha256:layer1"" + "path": "/var/lib/dpkg/status", + "layerID": "sha256:layer1" } ], - ""metadata"": { - ""maintainer"": ""Ubuntu Developers"" + "metadata": { + "maintainer": "Ubuntu Developers" } }, { - ""name"":""express"", - ""version"":""4.18.2"", - ""type"":""npm"", - ""locations"": [ + "name":"express", + "version":"4.18.2", + "type":"npm", + "locations": [ { - ""path"": ""/app/node_modules/express/package.json"", - ""layerID"": ""sha256:layer2"" + "path": "/app/node_modules/express/package.json", + "layerID": "sha256:layer2" } ], - ""metadata"": { - ""author"": ""TJ Holowaychuk"", - ""integrity"": ""sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ=="" + "metadata": { + "author": "TJ Holowaychuk", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==" } }, { - ""name"":""requests"", - ""version"":""2.31.0"", - ""type"":""python"", - ""locations"": [ + "name":"requests", + "version":"2.31.0", + "type":"python", + "locations": [ { - ""path"": ""/usr/local/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA"", - ""layerID"": ""sha256:layer2"" + "path": "/usr/local/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA", + "layerID": "sha256:layer2" } ], - ""metadata"": { - ""author"": ""Kenneth Reitz"", - ""license"": ""Apache-2.0"" + "metadata": { + "author": "Kenneth Reitz", + "license": "Apache-2.0" } } ] - }"; + } + """; this.mockDockerService.Setup(service => service.CreateAndRunContainerAsync( @@ -548,54 +554,55 @@ public async Task TestLinuxScanner_SupportsMultipleComponentTypes_Async() [TestMethod] public async Task TestLinuxScanner_FiltersComponentsByEnabledTypes_OnlyLinux_Async() { - const string syftOutputWithMixedTypes = - @"{ - ""distro"": { - ""id"":""ubuntu"", - ""versionId"":""22.04"" + const string syftOutputWithMixedTypes = """ + { + "distro": { + "id":"ubuntu", + "versionID":"22.04" }, - ""artifacts"": [ + "artifacts": [ { - ""name"":""curl"", - ""version"":""7.81.0-1ubuntu1.10"", - ""type"":""deb"", - ""locations"": [ + "name":"curl", + "version":"7.81.0-1ubuntu1.10", + "type":"deb", + "locations": [ { - ""path"": ""/var/lib/dpkg/status"", - ""layerID"": ""sha256:layer1"" + "path": "/var/lib/dpkg/status", + "layerID": "sha256:layer1" } ], - ""metadata"": { - ""maintainer"": ""Ubuntu Developers"" + "metadata": { + "maintainer": "Ubuntu Developers" } }, { - ""name"":""express"", - ""version"":""4.18.2"", - ""type"":""npm"", - ""locations"": [ + "name":"express", + "version":"4.18.2", + "type":"npm", + "locations": [ { - ""path"": ""/app/node_modules/express/package.json"", - ""layerID"": ""sha256:layer2"" + "path": "/app/node_modules/express/package.json", + "layerID": "sha256:layer2" } ], - ""metadata"": { - ""author"": ""TJ Holowaychuk"" + "metadata": { + "author": "TJ Holowaychuk" } }, { - ""name"":""requests"", - ""version"":""2.31.0"", - ""type"":""python"", - ""locations"": [ + "name":"requests", + "version":"2.31.0", + "type":"python", + "locations": [ { - ""path"": ""/usr/local/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA"", - ""layerID"": ""sha256:layer2"" + "path": "/usr/local/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA", + "layerID": "sha256:layer2" } ] } ] - }"; + } + """; this.mockDockerService.Setup(service => service.CreateAndRunContainerAsync( @@ -632,54 +639,55 @@ public async Task TestLinuxScanner_FiltersComponentsByEnabledTypes_OnlyLinux_Asy [TestMethod] public async Task TestLinuxScanner_FiltersComponentsByEnabledTypes_OnlyNpmAndPip_Async() { - const string syftOutputWithMixedTypes = - @"{ - ""distro"": { - ""id"":""ubuntu"", - ""versionId"":""22.04"" + const string syftOutputWithMixedTypes = """ + { + "distro": { + "id":"ubuntu", + "versionId":"22.04" }, - ""artifacts"": [ + "artifacts": [ { - ""name"":""curl"", - ""version"":""7.81.0-1ubuntu1.10"", - ""type"":""deb"", - ""locations"": [ + "name":"curl", + "version":"7.81.0-1ubuntu1.10", + "type":"deb", + "locations": [ { - ""path"": ""/var/lib/dpkg/status"", - ""layerID"": ""sha256:layer1"" + "path": "/var/lib/dpkg/status", + "layerID": "sha256:layer1" } ], - ""metadata"": { - ""maintainer"": ""Ubuntu Developers"" + "metadata": { + "maintainer": "Ubuntu Developers" } }, { - ""name"":""express"", - ""version"":""4.18.2"", - ""type"":""npm"", - ""locations"": [ + "name":"express", + "version":"4.18.2", + "type":"npm", + "locations": [ { - ""path"": ""/app/node_modules/express/package.json"", - ""layerID"": ""sha256:layer2"" + "path": "/app/node_modules/express/package.json", + "layerID": "sha256:layer2" } ], - ""metadata"": { - ""author"": ""TJ Holowaychuk"" + "metadata": { + "author": "TJ Holowaychuk" } }, { - ""name"":""requests"", - ""version"":""2.31.0"", - ""type"":""python"", - ""locations"": [ + "name":"requests", + "version":"2.31.0", + "type":"python", + "locations": [ { - ""path"": ""/usr/local/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA"", - ""layerID"": ""sha256:layer2"" + "path": "/usr/local/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA", + "layerID": "sha256:layer2" } ] } ] - }"; + } + """; this.mockDockerService.Setup(service => service.CreateAndRunContainerAsync(