github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/java.go (about)

     1  package pkg
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/anchore/syft/internal"
    10  	"github.com/anchore/syft/syft/file"
    11  )
    12  
    13  var jenkinsPluginPomPropertiesGroupIDs = []string{
    14  	"io.jenkins.plugins",
    15  	"org.jenkins.plugins",
    16  	"org.jenkins-ci.plugins",
    17  	"io.jenkins-ci.plugins",
    18  	"com.cloudbees.jenkins.plugins",
    19  }
    20  
    21  // JavaArchive encapsulates all Java ecosystem metadata for a package as well as an (optional) parent relationship.
    22  type JavaArchive struct {
    23  	VirtualPath    string             `json:"virtualPath" cyclonedx:"virtualPath"` // we need to include the virtual path in cyclonedx documents to prevent deduplication of jars within jars
    24  	Manifest       *JavaManifest      `mapstructure:"Manifest" json:"manifest,omitempty"`
    25  	PomProperties  *JavaPomProperties `mapstructure:"PomProperties" json:"pomProperties,omitempty" cyclonedx:"-"`
    26  	PomProject     *JavaPomProject    `mapstructure:"PomProject" json:"pomProject,omitempty"`
    27  	ArchiveDigests []file.Digest      `hash:"ignore" json:"digest,omitempty"`
    28  	Parent         *Package           `hash:"ignore" json:"-"` // note: the parent cannot be included in the minimal definition of uniqueness since this field is not reproducible in an encode-decode cycle (is lossy).
    29  }
    30  
    31  // JavaPomProperties represents the fields of interest extracted from a Java archive's pom.properties file.
    32  type JavaPomProperties struct {
    33  	Path       string            `mapstructure:"path" json:"path"`
    34  	Name       string            `mapstructure:"name" json:"name"`
    35  	GroupID    string            `mapstructure:"groupId" json:"groupId" cyclonedx:"groupID"`
    36  	ArtifactID string            `mapstructure:"artifactId" json:"artifactId" cyclonedx:"artifactID"`
    37  	Version    string            `mapstructure:"version" json:"version"`
    38  	Scope      string            `mapstructure:"scope" json:"scope,omitempty"`
    39  	Extra      map[string]string `mapstructure:",remain" json:"extraFields,omitempty"`
    40  }
    41  
    42  // JavaPomProject represents fields of interest extracted from a Java archive's pom.xml file. See https://maven.apache.org/ref/3.6.3/maven-model/maven.html for more details.
    43  type JavaPomProject struct {
    44  	Path        string         `json:"path"`
    45  	Parent      *JavaPomParent `json:"parent,omitempty"`
    46  	GroupID     string         `json:"groupId"`
    47  	ArtifactID  string         `json:"artifactId"`
    48  	Version     string         `json:"version"`
    49  	Name        string         `json:"name"`
    50  	Description string         `json:"description,omitempty"`
    51  	URL         string         `json:"url,omitempty"`
    52  }
    53  
    54  // JavaPomParent contains the fields within the <parent> tag in a pom.xml file
    55  type JavaPomParent struct {
    56  	GroupID    string `json:"groupId"`
    57  	ArtifactID string `json:"artifactId"`
    58  	Version    string `json:"version"`
    59  }
    60  
    61  // PkgTypeIndicated returns the package Type indicated by the data contained in the JavaPomProperties.
    62  func (p JavaPomProperties) PkgTypeIndicated() Type {
    63  	if internal.HasAnyOfPrefixes(p.GroupID, jenkinsPluginPomPropertiesGroupIDs...) || strings.Contains(p.GroupID, ".jenkins.plugin") {
    64  		return JenkinsPluginPkg
    65  	}
    66  
    67  	return JavaPkg
    68  }
    69  
    70  // JavaManifest represents the fields of interest extracted from a Java archive's META-INF/MANIFEST.MF file.
    71  type JavaManifest struct {
    72  	Main     KeyValues   `json:"main,omitempty"`
    73  	Sections []KeyValues `json:"sections,omitempty"`
    74  }
    75  
    76  type unmarshalJavaManifest JavaManifest
    77  
    78  type legacyJavaManifest struct {
    79  	Main          map[string]string            `json:"main"`
    80  	NamedSections map[string]map[string]string `json:"namedSections"`
    81  }
    82  
    83  func (m *JavaManifest) UnmarshalJSON(b []byte) error {
    84  	var either map[string]any
    85  	err := json.Unmarshal(b, &either)
    86  	if err != nil {
    87  		return fmt.Errorf("could not unmarshal java manifest: %w", err)
    88  	}
    89  	if _, ok := either["namedSections"]; ok {
    90  		var lm legacyJavaManifest
    91  		if err = json.Unmarshal(b, &lm); err != nil {
    92  			return fmt.Errorf("could not unmarshal java manifest: %w", err)
    93  		}
    94  		*m = lm.toNewManifest()
    95  		return nil
    96  	}
    97  	var jm unmarshalJavaManifest
    98  	err = json.Unmarshal(b, &jm)
    99  	if err != nil {
   100  		return fmt.Errorf("could not unmarshal java manifest: %w", err)
   101  	}
   102  	*m = JavaManifest(jm)
   103  	return nil
   104  }
   105  
   106  func (lm legacyJavaManifest) toNewManifest() JavaManifest {
   107  	var result JavaManifest
   108  	result.Main = keyValuesFromMap(lm.Main)
   109  	var sectionNames []string
   110  	for k := range lm.NamedSections {
   111  		sectionNames = append(sectionNames, k)
   112  	}
   113  	sort.Strings(sectionNames)
   114  	var sections []KeyValues
   115  	for _, name := range sectionNames {
   116  		section := KeyValues{
   117  			KeyValue{
   118  				Key:   "Name",
   119  				Value: name,
   120  			},
   121  		}
   122  		section = append(section, keyValuesFromMap(lm.NamedSections[name])...)
   123  		sections = append(sections, section)
   124  	}
   125  	result.Sections = sections
   126  	return result
   127  }
   128  
   129  func (m JavaManifest) Section(name string) KeyValues {
   130  	for _, section := range m.Sections {
   131  		if sectionName, ok := section.Get("Name"); ok && sectionName == name {
   132  			return section
   133  		}
   134  	}
   135  	return nil
   136  }