@@ -36,6 +36,58 @@ import tempfile
3636class ImageBuildError (Exception ):
3737 pass
3838
39+ # Parse email contact strings.
40+ def supplier_from_contact_string (raw ):
41+ if not raw :
42+ return None
43+ m = re .match (r'^(?P<name>.*?)\s*[<\[](?P<contact>[^<>\[\]]+)[>\]]$' , raw .strip ())
44+ if m :
45+ name , contact = m .group ('name' ).strip (), m .group ('contact' ).strip ()
46+ if name .lower () == 'none' or contact .lower () == 'none' :
47+ return None
48+ supplier = {}
49+ if name :
50+ supplier ['name' ] = name
51+ if '@' in contact :
52+ supplier ['contact' ] = [{'email' : contact }]
53+ return supplier or None
54+ if re .match (r'^[^\s@]+@[^\s@]+\.[^\s@]+$' , raw .strip ()):
55+ return {'contact' : [{'email' : raw .strip ()}]}
56+ return {'name' : raw .strip ()}
57+
58+ def component_supplier (comp , vyos_supplier , go_authors_supplier ):
59+ # Return the supplier field for CycloneDX components.
60+
61+ # Debian packages: the Maintainer field (component.publisher) is a real,
62+ # per-package supplier fact.
63+ supplier = supplier_from_contact_string (comp .get ('publisher' ))
64+ if supplier :
65+ return supplier
66+
67+ # Kernel modules: VyOS compiles every one itself for this exact kernel
68+ # build, so VyOS genuinely is the supplier of the binary - regardless of
69+ # who originally authored the driver source.
70+ found_by = next ((p ['value' ] for p in comp .get ('properties' , [])
71+ if p .get ('name' ) == 'syft:package:foundBy' ), None )
72+ if found_by == 'linux-kernel-cataloger' :
73+ return vyos_supplier
74+
75+ # Python packages: component.author (from the package's own METADATA) is
76+ # a real, per-package supplier fact. We install these packages unmodified.
77+ supplier = supplier_from_contact_string (comp .get ('author' ))
78+ if supplier :
79+ return supplier
80+
81+ # golang.org/x/* and the embedded Go stdlib: a verified official
82+ # supplier, every LICENSE file says "Copyright <year> The Go Authors".
83+ # Not extended to other Go modules (e.g. github.com/<org>/<repo>): unlike
84+ # this one prefix, there's no single verifiable fact covering all of them.
85+ name = comp .get ('name' , '' )
86+ if name .startswith ('golang.org/x/' ) or name == 'stdlib' :
87+ return go_authors_supplier
88+
89+ return None
90+
3991# argparse converts hyphens to underscores,
4092# so for lookups in the original options hash we have to convert them back
4193def field_to_option (s ):
@@ -809,6 +861,8 @@ Pin-Priority: 600
809861
810862 vyos_author = {'name' : 'VyOS maintainers and contributors' , 'email' : 'maintainers@vyos.io' }
811863 vyos_supplier = {'name' : 'VyOS Networks' , 'url' : [build_defaults ['website_url' ]]}
864+ go_authors_supplier = {'name' : 'The Go Authors' , 'url' : ['https://go.dev' ]}
865+
812866 cdx ['metadata' ]['authors' ] = [vyos_author ]
813867 cdx ['metadata' ]['supplier' ] = vyos_supplier
814868
@@ -818,72 +872,12 @@ Pin-Priority: 600
818872 cdx ['metadata' ]['component' ]['type' ] = 'operating-system'
819873 cdx ['metadata' ]['component' ]['supplier' ] = vyos_supplier
820874
821- # Add the correct supplier field for Debian packages.
822- publisher_re = re .compile (r'^(?P<name>.*?)\s*[<\[](?P<contact>[^<>\[\]]+)[>\]]$' )
823- email_re = re .compile (r'^[^\s@]+@[^\s@]+\.[^\s@]+$' )
824- for comp in cdx ['components' ]:
825- publisher = comp .get ('publisher' )
826- if not publisher :
827- continue
828- m = publisher_re .match (publisher .strip ())
829- if m :
830- name , contact = m .group ('name' ).strip (), m .group ('contact' ).strip ()
831- if name .lower () == 'none' or contact .lower () == 'none' :
832- continue
833- supplier = {}
834- if name :
835- supplier ['name' ] = name
836- if '@' in contact :
837- supplier ['contact' ] = [{'email' : contact }]
838- elif email_re .match (publisher .strip ()):
839- supplier = {'contact' : [{'email' : publisher .strip ()}]}
840- else :
841- supplier = {'name' : publisher .strip ()}
842- if supplier :
843- comp ['supplier' ] = supplier
844-
845- # VyOS compiles every kernel module itself, so it's correct to list VyOS as the supplier for these modules.
875+ # Add the correct component.supplier field.
846876 for comp in cdx ['components' ]:
847- if comp .get ('supplier' ):
848- continue
849- found_by = next ((p ['value' ] for p in comp .get ('properties' , [])
850- if p .get ('name' ) == 'syft:package:foundBy' ), None )
851- if found_by == 'linux-kernel-cataloger' :
852- comp ['supplier' ] = vyos_supplier
853-
854- # Use the component.author value as the supplier for Python packages.
855- for comp in cdx ['components' ]:
856- if comp .get ('supplier' ):
857- continue
858- author = comp .get ('author' )
859- if not author :
860- continue
861- m = publisher_re .match (author .strip ())
862- if m :
863- name , contact = m .group ('name' ).strip (), m .group ('contact' ).strip ()
864- if name .lower () == 'none' or contact .lower () == 'none' :
865- continue
866- supplier = {}
867- if name :
868- supplier ['name' ] = name
869- if '@' in contact :
870- supplier ['contact' ] = [{'email' : contact }]
871- elif email_re .match (author .strip ()):
872- supplier = {'contact' : [{'email' : author .strip ()}]}
873- else :
874- supplier = {'name' : author .strip ()}
877+ supplier = component_supplier (comp , vyos_supplier , go_authors_supplier )
875878 if supplier :
876879 comp ['supplier' ] = supplier
877880
878- # Specify the supplier for golang.org/x/* modules and the embedded Go stdlib.
879- go_authors_supplier = {'name' : 'The Go Authors' , 'url' : ['https://go.dev' ]}
880- for comp in cdx ['components' ]:
881- if comp .get ('supplier' ):
882- continue
883- name = comp .get ('name' , '' )
884- if name .startswith ('golang.org/x/' ) or name == 'stdlib' :
885- comp ['supplier' ] = go_authors_supplier
886-
887881 with open (cdx_file , 'w' ) as f :
888882 json .dump (cdx , f )
889883
0 commit comments