Skip to content

Commit dd12290

Browse files
committed
Reapply "stdenv: Add CPE fields to meta"
This reverts commit de74f9c.
1 parent f892cc7 commit dd12290

11 files changed

Lines changed: 364 additions & 1 deletion

File tree

doc/redirects.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,12 @@
225225
"sec-language-cosmic": [
226226
"index.html#sec-language-cosmic"
227227
],
228+
"sec-meta-identifiers": [
229+
"index.html#sec-meta-identifiers"
230+
],
231+
"sec-meta-identifiers-cpe": [
232+
"index.html#sec-meta-identifiers-cpe"
233+
],
228234
"sec-modify-via-packageOverrides": [
229235
"index.html#sec-modify-via-packageOverrides"
230236
],
@@ -628,6 +634,15 @@
628634
"var-go-buildTestBinaries": [
629635
"index.html#var-go-buildTestBinaries"
630636
],
637+
"var-meta-identifiers-cpe": [
638+
"index.html#var-meta-identifiers-cpe"
639+
],
640+
"var-meta-identifiers-cpeParts": [
641+
"index.html#var-meta-identifiers-cpeParts"
642+
],
643+
"var-meta-identifiers-possibleCPEs": [
644+
"index.html#var-meta-identifiers-possibleCPEs"
645+
],
631646
"var-meta-teams": [
632647
"index.html#var-meta-teams"
633648
],

doc/stdenv/meta.chapter.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,74 @@ Code to be executed on a peripheral device or embedded controller, built by a th
248248
### `lib.sourceTypes.binaryBytecode` {#lib.sourceTypes.binaryBytecode}
249249

250250
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.
251+
252+
## Software identifiers {#sec-meta-identifiers}
253+
254+
Package's `meta.identifiers` attribute specifies information about software identifiers associated with this package. Software identifiers are used, for example:
255+
* to generate Software Bill of Materials (SBOM) that lists all components used to build the software, which can later be used to perform vulnerability or license analysis of the resulting software;
256+
* to lookup software in different vulnerability databases or report new vulnerabilities to them.
257+
258+
Overriding the default `meta.identifiers` attribute is optional, but it is recommended to fill in pieces to help tools mentioned above get precise data.
259+
For example, we could get automatic notifications about potential vulnerabilities for users in the future.
260+
All identifiers specified in `meta.identifiers` are expected to be unambiguous and valid.
261+
262+
`meta.identifiers` contains `v1` attribute which is an attribute set that guarantees backward compatibility of its constituents. Right now it contains copies of all other attributes in `meta.identifiers`.
263+
264+
### CPE {#sec-meta-identifiers-cpe}
265+
266+
Common Platform Enumeration (CPE) is a specification maintained by NIST as part of the Security Content Automation Protocol (SCAP). It is used to identify software in National Vulnerabilities Database (NVD, https://nvd.nist.gov) and other vulnerability databases.
267+
268+
Current version of CPE 2.3 consists of 13 parts:
269+
270+
```
271+
cpe:2.3:a:<vendor>:<product>:<version>:<update>:<edition>:<language>:<sw_edition>:<target_sw>:<target_hw>:<other>
272+
```
273+
274+
Some of them are as follows:
275+
276+
* *CPE version* - current version of CPE is `2.3`
277+
* *part* - usually in Nixpkgs `a` for "application", can also be `o` for "operating system" or `h` for "hardware"
278+
* *vendor* - can point to the source of the package, or to Nixpkgs itself
279+
* *product* - name of the package
280+
* *version* - version of the package
281+
* *update* - name of the latest update, can be a patch version for semantically versioned packages
282+
* *edition* - any additional specification about the version
283+
284+
You can find information about all of these attributes in the [official specification](https://csrc.nist.gov/projects/security-content-automation-protocol/specifications/cpe/naming) (heading 5.3.3, pages 11-13).
285+
286+
Any fields that don't have a value are set to either `-` if the value is not available or `*` when the field can match any value.
287+
288+
For example, for glibc 2.40.1 CPE would be `cpe:2.3:a:gnu:glibc:2.40:1:*:*:*:*:*:*`.
289+
290+
#### `meta.identifiers.cpeParts` {#var-meta-identifiers-cpeParts}
291+
292+
This attribute contains an attribute set of all parts of the CPE for this package. Most of the parts default to `*` (match any value), with some exceptions:
293+
294+
* `part` defaults to `a` (application), can also be set to `o` for operating systems, for example, Linux kernel, or to `h` for hardware
295+
* `vendor` cannot be deduced from other sources, so it must be specified by the package author
296+
* `product` defaults to provided derivation's `pname` attribute and must be provided explicitly if `pname` is missing
297+
* `version` and `update` have no defaults and should be specified explicitly or using helper functions, when missing, `cpe` attribute will be empty, and all possible guesses using helper functions will be in `possibleCPEs` attribute.
298+
299+
It is up to the package author to make sure all parts are correct and match expected values in [NVD dictionary](https://nvd.nist.gov/products/cpe). Unknown values can be skipped, which would leave them with the default value of `*`.
300+
301+
Following functions help with filling out `version` and `update` fields:
302+
303+
* [`lib.meta.cpeFullVersionWithVendor`](#function-library-lib.meta.cpeFullVersionWithVendor)
304+
* [`lib.meta.cpePatchVersionInUpdateWithVendor`](#function-library-lib.meta.cpePatchVersionInUpdateWithVendor)
305+
306+
For many packages to make CPE available it should be enough to specify only:
307+
308+
```nix
309+
{
310+
# ...
311+
meta.identifiers.cpeParts = lib.meta.cpePatchVersionInUpdateWithVendor vendor version;
312+
}
313+
```
314+
315+
#### `meta.identifiers.cpe` {#var-meta-identifiers-cpe}
316+
317+
A readonly attribute that concatenates all CPE parts in one string.
318+
319+
#### `meta.identifiers.possibleCPEs` {#var-meta-identifiers-possibleCPEs}
320+
321+
A readonly attribute containing the list of guesses for what CPE for this package can look like. It includes all variants of version handling mentioned above. Each item is an attrset with attributes `cpeParts` and `cpe` for each guess.

lib/meta.nix

Lines changed: 188 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ let
1515
assertMsg
1616
;
1717
inherit (lib.attrsets) mapAttrs' filterAttrs;
18-
inherit (builtins) isString match typeOf;
18+
inherit (builtins)
19+
isString
20+
match
21+
typeOf
22+
elemAt
23+
;
1924

2025
in
2126
rec {
@@ -491,4 +496,186 @@ rec {
491496
assert assertMsg (match ".*/.*" y == null)
492497
"lib.meta.getExe': The second argument \"${y}\" is a nested path with a \"/\" character, but it should just be the name of the executable instead.";
493498
"${getBin x}/bin/${y}";
499+
500+
/**
501+
Generate [CPE parts](#var-meta-identifiers-cpeParts) from inputs. Copies `vendor` and `version` to the output, and sets `update` to `*`.
502+
503+
# Inputs
504+
505+
`vendor`
506+
507+
: package's vendor
508+
509+
`version`
510+
511+
: package's version
512+
513+
# Type
514+
515+
```
516+
cpeFullVersionWithVendor :: string -> string -> AttrSet
517+
```
518+
519+
# Examples
520+
:::{.example}
521+
## `lib.meta.cpeFullVersionWithVendor` usage example
522+
523+
```nix
524+
lib.meta.cpeFullVersionWithVendor "gnu" "1.2.3"
525+
=> {
526+
vendor = "gnu";
527+
version = "1.2.3";
528+
update = "*";
529+
}
530+
```
531+
532+
:::
533+
:::{.example}
534+
## `lib.meta.cpeFullVersionWithVendor` usage in derivations
535+
536+
```nix
537+
mkDerivation rec {
538+
version = "1.2.3";
539+
# ...
540+
meta = {
541+
# ...
542+
identifiers.cpeParts = lib.meta.cpeFullVersionWithVendor "gnu" version;
543+
};
544+
}
545+
```
546+
:::
547+
*/
548+
cpeFullVersionWithVendor = vendor: version: {
549+
inherit vendor version;
550+
update = "*";
551+
};
552+
553+
/**
554+
Alternate version of [`lib.meta.cpePatchVersionInUpdateWithVendor`](#function-library-lib.meta.cpePatchVersionInUpdateWithVendor).
555+
If `cpePatchVersionInUpdateWithVendor` succeeds, returns an attribute set with `success` set to `true` and `value` set to the result.
556+
Otherwise, `success` is set to `false` and `error` is set to the string representation of the error.
557+
558+
# Inputs
559+
560+
`vendor`
561+
562+
: package's vendor
563+
564+
`version`
565+
566+
: package's version
567+
568+
# Type
569+
570+
```
571+
tryCPEPatchVersionInUpdateWithVendor :: string -> string -> AttrSet
572+
```
573+
574+
# Examples
575+
:::{.example}
576+
## `lib.meta.tryCPEPatchVersionInUpdateWithVendor` usage example
577+
578+
```nix
579+
lib.meta.tryCPEPatchVersionInUpdateWithVendor "gnu" "1.2.3"
580+
=> {
581+
success = true;
582+
value = {
583+
vendor = "gnu";
584+
version = "1.2";
585+
update = "3";
586+
};
587+
}
588+
```
589+
590+
:::
591+
:::{.example}
592+
## `lib.meta.cpePatchVersionInUpdateWithVendor` error example
593+
594+
```nix
595+
lib.meta.tryCPEPatchVersionInUpdateWithVendor "gnu" "5.3p0"
596+
=> {
597+
success = false;
598+
error = "version 5.3p0 doesn't match regex `([0-9]+\\.[0-9]+)\\.([0-9]+)`";
599+
}
600+
```
601+
602+
:::
603+
*/
604+
tryCPEPatchVersionInUpdateWithVendor =
605+
vendor: version:
606+
let
607+
regex = "([0-9]+\\.[0-9]+)\\.([0-9]+)";
608+
# we have to call toString here in case version is an attrset with __toString attribute
609+
versionMatch = builtins.match regex (toString version);
610+
in
611+
if versionMatch == null then
612+
{
613+
success = false;
614+
error = "version ${version} doesn't match regex `${regex}`";
615+
}
616+
else
617+
{
618+
success = true;
619+
value = {
620+
inherit vendor;
621+
version = elemAt versionMatch 0;
622+
update = elemAt versionMatch 1;
623+
};
624+
};
625+
626+
/**
627+
Generate [CPE parts](#var-meta-identifiers-cpeParts) from inputs. Copies `vendor` to the result. When `version` matches `X.Y.Z` where all parts are numerical, sets `version` and `update` fields to `X.Y` and `Z`. Throws an error if the version doesn't match the expected template.
628+
629+
# Inputs
630+
631+
`vendor`
632+
633+
: package's vendor
634+
635+
`version`
636+
637+
: package's version
638+
639+
# Type
640+
641+
```
642+
cpePatchVersionInUpdateWithVendor :: string -> string -> AttrSet
643+
```
644+
645+
# Examples
646+
:::{.example}
647+
## `lib.meta.cpePatchVersionInUpdateWithVendor` usage example
648+
649+
```nix
650+
lib.meta.cpePatchVersionInUpdateWithVendor "gnu" "1.2.3"
651+
=> {
652+
vendor = "gnu";
653+
version = "1.2";
654+
update = "3";
655+
}
656+
```
657+
658+
:::
659+
:::{.example}
660+
## `lib.meta.cpePatchVersionInUpdateWithVendor` usage in derivations
661+
662+
```nix
663+
mkDerivation rec {
664+
version = "1.2.3";
665+
# ...
666+
meta = {
667+
# ...
668+
identifiers.cpeParts = lib.meta.cpePatchVersionInUpdateWithVendor "gnu" version;
669+
};
670+
}
671+
```
672+
673+
:::
674+
*/
675+
cpePatchVersionInUpdateWithVendor =
676+
vendor: version:
677+
let
678+
result = tryCPEPatchVersionInUpdateWithVendor vendor version;
679+
in
680+
if result.success then result.value else throw result.error;
494681
}

pkgs/applications/networking/sync/rsync/default.nix

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,10 @@ stdenv.mkDerivation rec {
9292
ivan
9393
];
9494
platforms = platforms.unix;
95+
identifiers.cpeParts = {
96+
vendor = "samba";
97+
inherit version;
98+
update = "-";
99+
};
95100
};
96101
}

pkgs/by-name/he/hello/package.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,6 @@ stdenv.mkDerivation (finalAttrs: {
5555
maintainers = with lib.maintainers; [ stv0g ];
5656
mainProgram = "hello";
5757
platforms = lib.platforms.all;
58+
identifiers.cpeParts.vendor = "gnu";
5859
};
5960
})

pkgs/development/compilers/gcc/common/meta.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ in
3030
teams = [ teams.gcc ];
3131
mainProgram = "${targetPrefix}gcc";
3232

33+
identifiers.cpeParts.vendor = "gnu";
3334
}

pkgs/development/compilers/gcc/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ pipe
419419
platforms
420420
teams
421421
mainProgram
422+
identifiers
422423
;
423424
};
424425
}

pkgs/development/compilers/llvm/common/common-let.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ rec {
3434
++ lib.platforms.riscv
3535
++ lib.platforms.m68k
3636
++ lib.platforms.loongarch64;
37+
38+
identifiers.cpeParts.vendor = "llvm";
3739
};
3840

3941
releaseInfo =

pkgs/os-specific/linux/kernel/manual-config.nix

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,13 @@ lib.makeOverridable (
546546
]
547547
++ lib.optional (lib.versionOlder version "5.19") "loongarch64-linux";
548548
timeout = 14400; # 4 hours
549+
identifiers.cpeParts = {
550+
part = "o";
551+
vendor = "linux";
552+
product = "linux_kernel";
553+
inherit version;
554+
update = "*";
555+
};
549556
}
550557
// extraMeta;
551558
};

pkgs/shells/bash/5.nix

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,5 +183,15 @@ lib.warnIf (withDocs != null)
183183
badPlatforms = [ lib.systems.inspect.patterns.isMinGW ];
184184
maintainers = [ ];
185185
mainProgram = "bash";
186+
identifiers.cpeParts =
187+
let
188+
versionSplit = lib.split "p" version;
189+
in
190+
{
191+
vendor = "gnu";
192+
product = "bash";
193+
version = lib.elemAt versionSplit 0;
194+
update = lib.elemAt versionSplit 2;
195+
};
186196
};
187197
}

0 commit comments

Comments
 (0)