diff --git a/pkg/attestation/crafter/materials/csaf.go b/pkg/attestation/crafter/materials/csaf.go
index f7749fce0..a19cff11f 100644
--- a/pkg/attestation/crafter/materials/csaf.go
+++ b/pkg/attestation/crafter/materials/csaf.go
@@ -129,5 +129,30 @@ func (i *CSAFCrafter) Craft(ctx context.Context, filepath string) (*api.Attestat
 		return nil, fmt.Errorf("invalid CSAF file: %w", ErrInvalidMaterialType)
 	}
 
-	return uploadAndCraft(ctx, i.input, i.backend, filepath, i.logger)
+	m, err := uploadAndCraft(ctx, i.input, i.backend, filepath, i.logger)
+	if err != nil {
+		return nil, err
+	}
+
+	i.injectAnnotations(m, documentMap)
+
+	return m, nil
+}
+
+func (i *CSAFCrafter) injectAnnotations(m *api.Attestation_Material, documentMap map[string]any) {
+	m.Annotations = make(map[string]string)
+
+	// extract vendor info
+	if tracking, ok := documentMap["tracking"].(map[string]any); ok {
+		if generator, ok := tracking["generator"].(map[string]any); ok {
+			if engine, ok := generator["engine"].(map[string]any); ok {
+				if name, ok := engine["name"].(string); ok {
+					m.Annotations[AnnotationToolNameKey] = name
+				}
+				if version, ok := engine["version"].(string); ok {
+					m.Annotations[AnnotationToolVersionKey] = version
+				}
+			}
+		}
+	}
 }
diff --git a/pkg/attestation/crafter/materials/cyclonedxjson.go b/pkg/attestation/crafter/materials/cyclonedxjson.go
index 80bd4a4f3..45366fda2 100644
--- a/pkg/attestation/crafter/materials/cyclonedxjson.go
+++ b/pkg/attestation/crafter/materials/cyclonedxjson.go
@@ -27,7 +27,6 @@ import (
 	api "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
 	"github.com/chainloop-dev/chainloop/pkg/casclient"
 	remotename "github.com/google/go-containerregistry/pkg/name"
-
 	"github.com/rs/zerolog"
 )
 
@@ -43,19 +42,38 @@ type CyclonedxJSONCrafter struct {
 	*crafterCommon
 }
 
-// mainComponentStruct internal struct to unmarshall the incoming CycloneDX JSON
-type mainComponentStruct struct {
-	Metadata struct {
-		Component struct {
-			Name       string `json:"name"`
-			Type       string `json:"type"`
-			Version    string `json:"version"`
-			Properties []struct {
-				Name  string `json:"name"`
-				Value string `json:"value"`
-			} `json:"properties"`
-		} `json:"component"`
-	} `json:"metadata"`
+// cyclonedxDoc internal struct to unmarshall the incoming CycloneDX JSON
+type cyclonedxDoc struct {
+	SpecVersion string          `json:"specVersion"`
+	Metadata    json.RawMessage `json:"metadata"`
+}
+
+type cyclonedxMetadataV14 struct {
+	Tools []struct {
+		Name    string `json:"name"`
+		Version string `json:"version"`
+	} `json:"tools"`
+	Component cyclonedxComponent `json:"component"`
+}
+
+type cyclonedxComponent struct {
+	Name       string `json:"name"`
+	Type       string `json:"type"`
+	Version    string `json:"version"`
+	Properties []struct {
+		Name  string `json:"name"`
+		Value string `json:"value"`
+	} `json:"properties"`
+}
+
+type cyclonedxMetadataV15 struct {
+	Tools struct {
+		Components []struct { // available from 1.5 onwards
+			Name    string `json:"name"`
+			Version string `json:"version"`
+		} `json:"components"`
+	} `json:"tools"`
+	Component cyclonedxComponent `json:"component"`
 }
 
 func NewCyclonedxJSONCrafter(materialSchema *schemaapi.CraftingSchema_Material, backend *casclient.CASBackend, l *zerolog.Logger) (*CyclonedxJSONCrafter, error) {
@@ -100,33 +118,62 @@ func (i *CyclonedxJSONCrafter) Craft(ctx context.Context, filePath string) (*api
 		},
 	}
 
-	// Include the main component information if available
-	mainComponent, err := i.extractMainComponent(f)
-	if err != nil {
-		i.logger.Debug().Err(err).Msg("error extracting main component from sbom, skipping...")
+	// parse the file to extract the main information
+	var doc cyclonedxDoc
+	if err = json.Unmarshal(f, &doc); err != nil {
+		i.logger.Debug().Err(err).Msg("error decoding file to extract main information, skipping ...")
 	}
 
-	// If the main component is available, include it in the material
-	if mainComponent != nil {
-		res.M.(*api.Attestation_Material_SbomArtifact).SbomArtifact.MainComponent = &api.Attestation_Material_SBOMArtifact_MainComponent{
-			Name:    mainComponent.name,
-			Kind:    mainComponent.kind,
-			Version: mainComponent.version,
+	switch doc.SpecVersion {
+	case "1.4":
+		var metaV14 cyclonedxMetadataV14
+		if err = json.Unmarshal(doc.Metadata, &metaV14); err != nil {
+			i.logger.Debug().Err(err).Msg("error decoding file to extract main information, skipping ...")
+		} else {
+			i.extractMetadata(m, &metaV14)
+		}
+	default: // 1.5 onwards
+		var metaV15 cyclonedxMetadataV15
+		if err = json.Unmarshal(doc.Metadata, &metaV15); err != nil {
+			i.logger.Debug().Err(err).Msg("error decoding file to extract main information, skipping ...")
+		} else {
+			i.extractMetadata(m, &metaV15)
 		}
 	}
 
 	return res, nil
 }
 
-// extractMainComponent inspects the SBOM and extracts the main component if any and available
-func (i *CyclonedxJSONCrafter) extractMainComponent(rawFile []byte) (*SBOMMainComponentInfo, error) {
-	var mainComponent mainComponentStruct
-	err := json.Unmarshal(rawFile, &mainComponent)
-	if err != nil {
-		return nil, fmt.Errorf("error extracting main component: %w", err)
+func (i *CyclonedxJSONCrafter) extractMetadata(m *api.Attestation_Material, metadata any) {
+	m.Annotations = make(map[string]string)
+
+	switch meta := metadata.(type) {
+	case *cyclonedxMetadataV14:
+		if err := i.extractMainComponent(m, &meta.Component); err != nil {
+			i.logger.Debug().Err(err).Msg("error extracting main component from sbom, skipping...")
+		}
+
+		if len(meta.Tools) > 0 {
+			m.Annotations[AnnotationToolNameKey] = meta.Tools[0].Name
+			m.Annotations[AnnotationToolVersionKey] = meta.Tools[0].Version
+		}
+	case *cyclonedxMetadataV15:
+		if err := i.extractMainComponent(m, &meta.Component); err != nil {
+			i.logger.Debug().Err(err).Msg("error extracting main component from sbom, skipping...")
+		}
+
+		if len(meta.Tools.Components) > 0 {
+			m.Annotations[AnnotationToolNameKey] = meta.Tools.Components[0].Name
+			m.Annotations[AnnotationToolVersionKey] = meta.Tools.Components[0].Version
+		}
+	default:
+		i.logger.Debug().Msg("unknown metadata version")
 	}
+}
 
-	component := mainComponent.Metadata.Component
+// extractMainComponent inspects the SBOM and extracts the main component if any and available
+func (i *CyclonedxJSONCrafter) extractMainComponent(m *api.Attestation_Material, component *cyclonedxComponent) error {
+	var mainComponent *SBOMMainComponentInfo
 
 	// If the version is empty, try to extract it from the properties
 	if component.Version == "" {
@@ -141,23 +188,32 @@ func (i *CyclonedxJSONCrafter) extractMainComponent(rawFile []byte) (*SBOMMainCo
 	}
 
 	if component.Type != containerComponentKind {
-		return &SBOMMainComponentInfo{
+		mainComponent = &SBOMMainComponentInfo{
 			name:    component.Name,
 			kind:    component.Type,
 			version: component.Version,
-		}, nil
+		}
+	} else {
+		// Standardize the name to have the full repository name including the registry and
+		// sanitize the name to remove the possible tag from the repository name
+		ref, err := remotename.ParseReference(component.Name)
+		if err != nil {
+			return fmt.Errorf("couldn't parse OCI image repository name: %w", err)
+		}
+
+		mainComponent = &SBOMMainComponentInfo{
+			name:    ref.Context().String(),
+			kind:    component.Type,
+			version: component.Version,
+		}
 	}
 
-	// Standardize the name to have the full repository name including the registry and
-	// sanitize the name to remove the possible tag from the repository name
-	ref, err := remotename.ParseReference(component.Name)
-	if err != nil {
-		return nil, fmt.Errorf("couldn't parse OCI image repository name: %w", err)
+	// If the main component is available, include it in the material
+	m.M.(*api.Attestation_Material_SbomArtifact).SbomArtifact.MainComponent = &api.Attestation_Material_SBOMArtifact_MainComponent{
+		Name:    mainComponent.name,
+		Kind:    mainComponent.kind,
+		Version: mainComponent.version,
 	}
 
-	return &SBOMMainComponentInfo{
-		name:    ref.Context().String(),
-		kind:    component.Type,
-		version: component.Version,
-	}, nil
+	return nil
 }
diff --git a/pkg/attestation/crafter/materials/cyclonedxjson_test.go b/pkg/attestation/crafter/materials/cyclonedxjson_test.go
index 466601970..95d3d949d 100644
--- a/pkg/attestation/crafter/materials/cyclonedxjson_test.go
+++ b/pkg/attestation/crafter/materials/cyclonedxjson_test.go
@@ -73,6 +73,7 @@ func TestCyclonedxJSONCraft(t *testing.T) {
 		wantMainComponent        string
 		wantMainComponentKind    string
 		wantMainComponentVersion string
+		annotations              map[string]string
 	}{
 		{
 			name:     "invalid path",
@@ -96,6 +97,10 @@ func TestCyclonedxJSONCraft(t *testing.T) {
 			wantFilename:          "sbom.cyclonedx.json",
 			wantMainComponent:     ".",
 			wantMainComponentKind: "file",
+			annotations: map[string]string{
+				"chainloop.material.tool.name":    "syft",
+				"chainloop.material.tool.version": "0.73.0",
+			},
 		},
 		{
 			name:                     "1.5 version",
@@ -105,6 +110,10 @@ func TestCyclonedxJSONCraft(t *testing.T) {
 			wantMainComponent:        "ghcr.io/chainloop-dev/chainloop/control-plane",
 			wantMainComponentKind:    "container",
 			wantMainComponentVersion: "v0.55.0",
+			annotations: map[string]string{
+				"chainloop.material.tool.name":    "syft",
+				"chainloop.material.tool.version": "0.101.1",
+			},
 		},
 	}
 
@@ -151,6 +160,12 @@ func TestCyclonedxJSONCraft(t *testing.T) {
 				},
 				got.GetSbomArtifact(),
 			)
+
+			if tc.annotations != nil {
+				for k, v := range tc.annotations {
+					ast.Equal(v, got.Annotations[k])
+				}
+			}
 		})
 	}
 }
diff --git a/pkg/attestation/crafter/materials/materials.go b/pkg/attestation/crafter/materials/materials.go
index d71127ee3..45b4e8188 100644
--- a/pkg/attestation/crafter/materials/materials.go
+++ b/pkg/attestation/crafter/materials/materials.go
@@ -34,6 +34,9 @@ import (
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
+const AnnotationToolNameKey = "chainloop.material.tool.name"
+const AnnotationToolVersionKey = "chainloop.material.tool.version"
+
 var (
 	// ErrInvalidMaterialType is returned when the provided material type
 	// is not from the kind we are expecting
@@ -232,7 +235,9 @@ func Craft(ctx context.Context, materialSchema *schemaapi.CraftingSchema_Materia
 	}
 
 	m.AddedAt = timestamppb.New(time.Now())
-	m.Annotations = make(map[string]string)
+	if m.Annotations == nil {
+		m.Annotations = make(map[string]string)
+	}
 
 	for _, annotation := range materialSchema.Annotations {
 		m.Annotations[annotation.Name] = annotation.Value
diff --git a/pkg/attestation/crafter/materials/sarif.go b/pkg/attestation/crafter/materials/sarif.go
index 2cf48d849..0c64cfe15 100644
--- a/pkg/attestation/crafter/materials/sarif.go
+++ b/pkg/attestation/crafter/materials/sarif.go
@@ -54,5 +54,24 @@ func (i *SARIFCrafter) Craft(ctx context.Context, filepath string) (*api.Attesta
 		return nil, fmt.Errorf("invalid SARIF file: %w", ErrInvalidMaterialType)
 	}
 
-	return uploadAndCraft(ctx, i.input, i.backend, filepath, i.logger)
+	m, err := uploadAndCraft(ctx, i.input, i.backend, filepath, i.logger)
+	if err != nil {
+		return nil, err
+	}
+
+	i.injectAnnotations(m, doc)
+
+	return m, nil
+}
+
+func (i *SARIFCrafter) injectAnnotations(m *api.Attestation_Material, doc *sarif.Report) {
+	// add vendor information
+	if len(doc.Runs) > 0 {
+		// assuming vendor from first run.
+		m.Annotations = make(map[string]string)
+		m.Annotations[AnnotationToolNameKey] = doc.Runs[0].Tool.Driver.Name
+		if doc.Runs[0].Tool.Driver.Version != nil {
+			m.Annotations[AnnotationToolVersionKey] = *doc.Runs[0].Tool.Driver.Version
+		}
+	}
 }
diff --git a/pkg/attestation/crafter/materials/spdxjson.go b/pkg/attestation/crafter/materials/spdxjson.go
index fda808f9a..1809e134b 100644
--- a/pkg/attestation/crafter/materials/spdxjson.go
+++ b/pkg/attestation/crafter/materials/spdxjson.go
@@ -19,11 +19,13 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"strings"
 
 	schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
 	api "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
 	"github.com/chainloop-dev/chainloop/pkg/casclient"
 	"github.com/spdx/tools-golang/json"
+	"github.com/spdx/tools-golang/spdx"
 
 	"github.com/rs/zerolog"
 )
@@ -52,11 +54,36 @@ func (i *SPDXJSONCrafter) Craft(ctx context.Context, filePath string) (*api.Atte
 	defer f.Close()
 
 	// Decode the file to check it's a valid SPDX BOM
-	_, err = json.Read(f)
+	doc, err := json.Read(f)
 	if err != nil {
 		i.logger.Debug().Err(err).Msg("error decoding file")
 		return nil, fmt.Errorf("invalid spdx sbom file: %w", ErrInvalidMaterialType)
 	}
 
-	return uploadAndCraft(ctx, i.input, i.backend, filePath, i.logger)
+	m, err := uploadAndCraft(ctx, i.input, i.backend, filePath, i.logger)
+	if err != nil {
+		return nil, err
+	}
+
+	i.injectAnnotations(m, doc)
+
+	return m, nil
+}
+
+func (i *SPDXJSONCrafter) injectAnnotations(m *api.Attestation_Material, doc *spdx.Document) {
+	for _, c := range doc.CreationInfo.Creators {
+		if c.CreatorType == "Tool" {
+			m.Annotations = make(map[string]string)
+			m.Annotations[AnnotationToolNameKey] = c.Creator
+
+			// try to extract the tool name and version
+			// e.g. "myTool-1.0.0"
+			parts := strings.SplitN(c.Creator, "-", 2)
+			if len(parts) == 2 {
+				m.Annotations[AnnotationToolNameKey] = parts[0]
+				m.Annotations[AnnotationToolVersionKey] = parts[1]
+			}
+			break
+		}
+	}
 }