diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 42773982..73770aaf 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -55,10 +55,22 @@ jobs: curl -LO https://github.com/oras-project/oras/releases/download/v0.12.0/oras_0.12.0_linux_amd64.tar.gz tar -xvf ./oras_0.12.0_linux_amd64.tar.gz + - name: Get the UpdatedAt timestamp + id: timestamp + run: | + set -x + trap "rm -rf" /tmp/trivy-db-extract + rm -rf /tmp/trivy-db-extract + mkdir -p /tmp/trivy-db-extract/db + tar -zxvf db.tar.gz -C /tmp/trivy-db-extract/db + echo "timestamp=$(./trivy-db timestamp --cache-dir=/tmp/trivy-db-extract)" >> $GITHUB_OUTPUT + - name: Upload assets to GHCR + env: + TIMESTAMP_TAG: ${{ steps.timestamp.timestamp }} run: | ./oras version - tags=(latest ${{ env.VERSION }}) + tags=(latest ${{ env.VERSION }} ${TIMESTAMP_TAG}) for tag in ${tags[@]}; do ./oras push ghcr.io/${{ github.repository }}:${tag} \ --manifest-config /dev/null:application/vnd.aquasec.trivy.config.v1+json \ diff --git a/pkg/app.go b/pkg/app.go index 390946b0..9d0ad785 100644 --- a/pkg/app.go +++ b/pkg/app.go @@ -47,6 +47,24 @@ func (ac *AppConfig) NewApp(version string) *cli.App { }, }, }, + { + Name: "timestamp", + Usage: "retrieve the UpdatedAt timestamp", + Action: timestamp, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "cache-dir", + Usage: "cache directory path", + Value: utils.CacheDir(), + }, + cli.DurationFlag{ + Name: "update-interval", + Usage: "update interval", + Value: 24 * time.Hour, + EnvVar: "UPDATE_INTERVAL", + }, + }, + }, } return app diff --git a/pkg/timestamp.go b/pkg/timestamp.go new file mode 100644 index 00000000..fa985ae8 --- /dev/null +++ b/pkg/timestamp.go @@ -0,0 +1,25 @@ +package pkg + +import ( + "fmt" + + "github.com/aquasecurity/trivy-db/pkg/db" + "github.com/aquasecurity/trivy-db/pkg/vulndb" + "github.com/urfave/cli" + "golang.org/x/xerrors" +) + +func timestamp(c *cli.Context) error { + cacheDir := c.String("cache-dir") + if err := db.Init(cacheDir); err != nil { + return xerrors.Errorf("db initialize error: %w", err) + } + updateInterval := c.Duration("update-interval") + vdb := vulndb.New(cacheDir, updateInterval) + ts, err := vdb.Timestamp() + if err != nil { + return err + } + fmt.Println(ts) + return nil +} diff --git a/pkg/vulndb/db.go b/pkg/vulndb/db.go index 30a8d569..8452f4b7 100644 --- a/pkg/vulndb/db.go +++ b/pkg/vulndb/db.go @@ -1,6 +1,7 @@ package vulndb import ( + "fmt" "log" "time" @@ -114,6 +115,16 @@ func (t TrivyDB) Build(targets []string) error { return nil } +// Return a timestamp that would be a valid OCI tag (e.g. 2022-11-17-18-09-07) +func (t TrivyDB) Timestamp() (string, error) { + meta, err := t.metadata.Get() + if err != nil { + return "", err + } + u := meta.UpdatedAt + return fmt.Sprintf("%.4d-%.2d-%.2d-%.2d-%.2d-%.2d", u.Year(), u.Month(), u.Day(), u.Hour(), u.Minute(), u.Second()), nil +} + func (t TrivyDB) vulnSrc(target string) (vulnsrc.VulnSrc, bool) { for _, src := range t.vulnSrcs { if target == string(src.Name()) { diff --git a/pkg/vulndb/db_test.go b/pkg/vulndb/db_test.go index 1657239a..9de247c6 100644 --- a/pkg/vulndb/db_test.go +++ b/pkg/vulndb/db_test.go @@ -2,8 +2,10 @@ package vulndb_test import ( "encoding/json" + "fmt" "os" "path/filepath" + "regexp" "strings" "testing" "time" @@ -208,6 +210,15 @@ func TestTrivyDB_Build(t *testing.T) { dbtest.NoBucket(t, dbPath, []string{"advisory-detail"}) dbtest.NoBucket(t, dbPath, []string{"vulnerability-detail"}) dbtest.NoBucket(t, dbPath, []string{"vulnerability-id"}) + + // Check if timestamp is in correct format + timestamp, err := full.Timestamp() + require.NoError(t, err) + pattern := `\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}` + timestampIsValid, err := regexp.MatchString(pattern, timestamp) + require.NoError(t, err) + assert.True(t, timestampIsValid, + fmt.Sprintf("timestamp %s did not match regex %s", timestamp, pattern)) }) } }