github.com/yogeshkumararora/slsa-github-generator@v1.10.1-0.20240520161934-11278bd5afb4/internal/builders/go/main.go (about)

     1  // Copyright 2022 SLSA Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"bytes"
    19  	"crypto/sha256"
    20  	"encoding/hex"
    21  	"flag"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"os/exec"
    26  
    27  	"github.com/yogeshkumararora/slsa-github-generator/github"
    28  	"github.com/yogeshkumararora/slsa-github-generator/signing/sigstore"
    29  
    30  	// Enable the GitHub OIDC auth provider.
    31  	_ "github.com/sigstore/cosign/v2/pkg/providers/github"
    32  
    33  	"github.com/yogeshkumararora/slsa-github-generator/internal/builders/go/pkg"
    34  	"github.com/yogeshkumararora/slsa-github-generator/internal/utils"
    35  )
    36  
    37  func usage(p string) {
    38  	panic(fmt.Sprintf(`Usage:
    39  	 %s build [--dry] slsa-releaser.yml
    40  	 %s provenance --binary-name $NAME --digest $DIGEST --command $COMMAND --env $ENV`, p, p))
    41  }
    42  
    43  func check(e error) {
    44  	if e != nil {
    45  		fmt.Fprint(os.Stderr, e.Error())
    46  		os.Exit(1)
    47  	}
    48  }
    49  
    50  func runBuild(dry bool, configFile, evalEnvs string) error {
    51  	goc, err := exec.LookPath("go")
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	cfg, err := pkg.ConfigFromFile(configFile)
    57  	if err != nil {
    58  		return err
    59  	}
    60  	fmt.Println(cfg)
    61  
    62  	gobuild := pkg.GoBuildNew(goc, cfg)
    63  
    64  	// Set env variables encoded as arguments.
    65  	err = gobuild.SetArgEnvVariables(evalEnvs)
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	err = gobuild.Run(dry)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	return nil
    76  }
    77  
    78  func runProvenanceGeneration(subject, digest, commands, envs, workingDir, rekor string) error {
    79  	r := sigstore.NewRekor(rekor)
    80  	s := sigstore.NewDefaultFulcio()
    81  	attBytes, err := pkg.GenerateProvenance(subject, digest,
    82  		commands, envs, workingDir, s, r, nil)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	filename := fmt.Sprintf("%s.intoto.jsonl", subject)
    88  	f, err := utils.CreateNewFileUnderCurrentDirectory(filename, os.O_WRONLY)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	_, err = f.Write(attBytes)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	if err := github.SetOutput("signed-provenance-name", filename); err != nil {
    98  		return err
    99  	}
   100  
   101  	h, err := computeSHA256(attBytes)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	return github.SetOutput("signed-provenance-sha256", h)
   107  }
   108  
   109  func main() {
   110  	// Build command.
   111  	buildCmd := flag.NewFlagSet("build", flag.ExitOnError)
   112  	buildDry := buildCmd.Bool("dry", false, "dry run of the build without invoking compiler")
   113  
   114  	// Provenance command.
   115  	provenanceCmd := flag.NewFlagSet("provenance", flag.ExitOnError)
   116  	provenanceName := provenanceCmd.String("binary-name", "", "untrusted binary name of the artifact built")
   117  	provenanceDigest := provenanceCmd.String("digest", "", "sha256 digest of the untrusted binary")
   118  	provenanceCommand := provenanceCmd.String("command", "", "command used to compile the binary")
   119  	provenanceEnv := provenanceCmd.String("env", "", "env variables used to compile the binary")
   120  	provenanceWorkingDir := provenanceCmd.String("workingDir", "", "working directory used to issue compilation commands")
   121  	provenanceRekor := provenanceCmd.String("rekor", sigstore.DefaultRekorAddr, "rekor server to use for provenance")
   122  
   123  	// Expect a sub-command.
   124  	if len(os.Args) < 2 {
   125  		usage(os.Args[0])
   126  	}
   127  
   128  	switch os.Args[1] {
   129  	case buildCmd.Name():
   130  		check(buildCmd.Parse(os.Args[2:]))
   131  		if len(buildCmd.Args()) < 1 {
   132  			usage(os.Args[0])
   133  		}
   134  		configFile := buildCmd.Args()[0]
   135  		evaluatedEnvs := buildCmd.Args()[1]
   136  
   137  		check(runBuild(*buildDry, configFile, evaluatedEnvs))
   138  
   139  	case provenanceCmd.Name():
   140  		check(provenanceCmd.Parse(os.Args[2:]))
   141  		// Note: *provenanceEnv may be empty.
   142  		if *provenanceName == "" || *provenanceDigest == "" ||
   143  			*provenanceCommand == "" || *provenanceWorkingDir == "" {
   144  			usage(os.Args[0])
   145  		}
   146  
   147  		err := runProvenanceGeneration(*provenanceName, *provenanceDigest,
   148  			*provenanceCommand, *provenanceEnv, *provenanceWorkingDir, *provenanceRekor)
   149  		check(err)
   150  
   151  	default:
   152  		fmt.Println("expected 'build' or 'provenance' subcommands")
   153  		os.Exit(1)
   154  	}
   155  }
   156  
   157  func computeSHA256(data []byte) (string, error) {
   158  	hash := sha256.New()
   159  	if _, err := io.Copy(hash, bytes.NewReader(data)); err != nil {
   160  		return "", err
   161  	}
   162  	return hex.EncodeToString(hash.Sum(nil)), nil
   163  }