diff --git a/doc/stdenv/meta.chapter.md b/doc/stdenv/meta.chapter.md index c1bb3f8863fc401..475006b1259b66f 100644 --- a/doc/stdenv/meta.chapter.md +++ b/doc/stdenv/meta.chapter.md @@ -249,3 +249,31 @@ Unfree package that cannot be redistributed. You can build it yourself, but you ### `lib.licenses.unfreeRedistributableFirmware`, `"unfree-redistributable-firmware"` {#lib.licenses.unfreeredistributablefirmware-unfree-redistributable-firmware} This package supplies unfree, redistributable firmware. This is a separate value from `unfree-redistributable` because not everybody cares whether firmware is free. + +## Source provenance {#sec-meta-sourceProvenance} + +The value of a package's `meta.sourceProvenance` attribute specifies the provenance of the package's derivation outputs. + +If a package contains elements that are not built from the original source by a nixpkgs derivation, the `meta.sourceProvenance` attribute should be a list containing one or more value from `lib.sourceTypes` defined in [`nixpkgs/lib/source-types.nix`](https://github.com/NixOS/nixpkgs/blob/master/lib/source-types.nix). + +Adding this information helps users who have needs related to build transparency and supply-chain security to gain some visibility into their installed software or set policy to allow or disallow installation based on source provenance. + +The presence of a particular `sourceType` in a package's `meta.sourceProvenance` list indicates that the package contains some components falling into that category, though the *absence* of that `sourceType` does not *guarantee* the absence of that category of `sourceType` in the package's contents. A package with no `meta.sourceProvenance` set implies it has no *known* `sourceType`s other than `fromSource`. + +The meaning of the `meta.sourceProvenance` attribute does not depend on the value of the `meta.license` attribute. + +### `lib.sourceTypes.fromSource` {#lib.sourceTypes.fromSource} + +Package elements which are produced by a nixpkgs derivation which builds them from source code. + +### `lib.sourceTypes.binaryNativeCode` {#lib.sourceTypes.binaryNativeCode} + +Native code to be executed on the target system's CPU, built by a third party. This includes packages which wrap a downloaded AppImage or Debian package. + +### `lib.sourceTypes.binaryFirmware` {#lib.sourceTypes.binaryFirmware} + +Code to be executed on a peripheral device or embedded controller, built by a third party. + +### `lib.sourceTypes.binaryBytecode` {#lib.sourceTypes.binaryBytecode} + +Code to run on a VM interpreter or JIT compiled into bytecode by a third party. This includes packages which download Java `.jar` files from another source. diff --git a/lib/default.nix b/lib/default.nix index e919509e724a5a9..791eba8a9301fe3 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -36,6 +36,7 @@ let # constants licenses = callLibs ./licenses.nix; + sourceTypes = callLibs ./source-types.nix; systems = callLibs ./systems; # serialization diff --git a/lib/source-types.nix b/lib/source-types.nix new file mode 100644 index 000000000000000..c4f263dcf4643c7 --- /dev/null +++ b/lib/source-types.nix @@ -0,0 +1,19 @@ +{ lib }: + +let + defaultSourceType = tname: { + shortName = tname; + isSource = false; + }; +in lib.mapAttrs (tname: tset: defaultSourceType tname // tset) { + + fromSource = { + isSource = true; + }; + + binaryNativeCode = {}; + + binaryBytecode = {}; + + binaryFirmware = {}; +} diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix index 1da098dabbee18a..4e5db210637a6e6 100644 --- a/pkgs/stdenv/generic/check-meta.nix +++ b/pkgs/stdenv/generic/check-meta.nix @@ -20,6 +20,9 @@ let allowUnfree = config.allowUnfree || builtins.getEnv "NIXPKGS_ALLOW_UNFREE" == "1"; + allowNonSource = config.allowNonSource or true + || builtins.getEnv "NIXPKGS_ALLOW_NONSOURCE" == "1"; + allowlist = config.allowlistedLicenses or config.whitelistedLicenses or []; blocklist = config.blocklistedLicenses or config.blacklistedLicenses or []; @@ -86,12 +89,39 @@ let allowInsecurePredicate attrs || builtins.getEnv "NIXPKGS_ALLOW_INSECURE" == "1"; - showLicense = license: toString (map (l: l.shortName or "unknown") (lib.lists.toList license)); + + isNonSource = sourceTypes: lib.lists.any (t: !t.isSource) sourceTypes; + + hasNonSourceProvenance = attrs: + (attrs ? meta.sourceProvenance) && + isNonSource (lib.lists.toList attrs.meta.sourceProvenance); + + # Allow granular checks to allow only some non-source-built packages + # Example: + # {pkgs, ...}: + # { + # allowNonSource = false; + # allowNonSourcePredicate = with lib.lists; pkg: !(any (p: !p.isSource && p!=lib.sourceTypes.binaryFirmware) (toList pkg.meta.sourceProvenance)); + # } + allowNonSourcePredicate = config.allowNonSourcePredicate or (x: false); + + # Check whether non-source packages are allowed and if not, whether the + # package has non-source provenance and is not explicitly allowed by the + # `allowNonSourcePredicate` function. + hasDeniedNonSourceProvenance = attrs: + hasNonSourceProvenance attrs && + !allowNonSource && + !allowNonSourcePredicate attrs; + + showLicenseOrSourceType = value: toString (map (v: v.shortName or "unknown") (lib.lists.toList value)); + showLicense = showLicenseOrSourceType; + showSourceType = showLicenseOrSourceType; pos_str = meta: meta.position or "«unknown-file»"; remediation = { - unfree = remediate_allowlist "Unfree" remediate_unfree_predicate; + unfree = remediate_allowlist "Unfree" (remediate_predicate "allowUnfreePredicate"); + non-source = remediate_allowlist "NonSource" (remediate_predicate "allowNonSourcePredicate"); broken = remediate_allowlist "Broken" (x: ""); unsupported = remediate_allowlist "UnsupportedSystem" (x: ""); blocklisted = x: ""; @@ -104,17 +134,19 @@ let Unfree = "NIXPKGS_ALLOW_UNFREE"; Broken = "NIXPKGS_ALLOW_BROKEN"; UnsupportedSystem = "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM"; + NonSource = "NIXPKGS_ALLOW_NONSOURCE"; }.${allow_attr}; remediation_phrase = allow_attr: { Unfree = "unfree packages"; Broken = "broken packages"; UnsupportedSystem = "packages that are unsupported for this system"; + NonSource = "packages not built from source"; }.${allow_attr}; - remediate_unfree_predicate = attrs: + remediate_predicate = predicateConfigAttr: attrs: '' Alternatively you can configure a predicate to allow specific packages: - { nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ + { nixpkgs.config.${predicateConfigAttr} = pkg: builtins.elem (lib.getName pkg) [ "${lib.getName attrs}" ]; } @@ -226,6 +258,7 @@ let downloadPage = str; changelog = either (listOf str) str; license = either (listOf lib.types.attrs) (either lib.types.attrs str); + sourceProvenance = either (listOf lib.types.attrs) lib.types.attrs; maintainers = listOf (attrsOf str); priority = int; platforms = listOf str; @@ -288,6 +321,7 @@ let checkValidity = attrs: { unfree = hasUnfreeLicense attrs; + nonSource = hasNonSourceProvenance attrs; broken = isMarkedBroken attrs; unsupported = hasUnsupportedPlatform attrs; insecure = isMarkedInsecure attrs; @@ -296,6 +330,8 @@ let { valid = "no"; reason = "unfree"; errormsg = "has an unfree license (‘${showLicense attrs.meta.license}’)"; } else if hasBlocklistedLicense attrs then { valid = "no"; reason = "blocklisted"; errormsg = "has a blocklisted license (‘${showLicense attrs.meta.license}’)"; } + else if hasDeniedNonSourceProvenance attrs then + { valid = "no"; reason = "non-source"; errormsg = "contains elements not built from source (‘${showSourceType attrs.meta.sourceProvenance}’)"; } else if !allowBroken && attrs.meta.broken or false then { valid = "no"; reason = "broken"; errormsg = "is marked as broken"; } else if !allowUnsupportedSystem && hasUnsupportedPlatform attrs then