Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/scripts/deployment-monitor/capture-homebrew-metadata.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Captures version and sha256 metadata for the Homebrew distribution channel (macOS).
# Required env: SNYK_VERSION_DIR, METADATA_SUFFIX

if [[ -n "${CI:-}" ]]; then
set -euo pipefail
else
set -exuo pipefail
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HELPERS_DIR="${SCRIPT_DIR}/helpers"

: "${SNYK_VERSION_DIR:?SNYK_VERSION_DIR is required}"
: "${METADATA_SUFFIX:?METADATA_SUFFIX is required}"

VERSION="$(snyk --version | tr -d '\r\n')"
SNYK_PATH="$(command -v snyk)"
SHA256="$(shasum -a 256 "$SNYK_PATH" | awk '{print $1}')"
BINARY_NAME="$(
SNYK_PATH="$SNYK_PATH" bash "${HELPERS_DIR}/resolve-macos-binary-name.sh"
)"

RELEASE_JSON="${GITHUB_WORKSPACE:-$(pwd)}/release.json"
RELEASE_URL="https://static.snyk.io/cli/v${VERSION}/release.json"
curl --retry 2 -sSL "$RELEASE_URL" -o "$RELEASE_JSON"
EXPECTED="$(
BINARY_NAME="$BINARY_NAME" go run -C "${SCRIPT_DIR}" ./cmd/extract-release-json-hash "$RELEASE_JSON"
)"

if [ -z "$EXPECTED" ]; then
echo "Missing expected hash for ${BINARY_NAME} in release.json."
exit 1
fi

if [ "$SHA256" != "$EXPECTED" ]; then
echo "Hash mismatch for ${BINARY_NAME} (homebrew)."
echo "Expected: $EXPECTED"
echo "Actual: $SHA256"
exit 1
fi

SNYK_VERSION_DIR="$SNYK_VERSION_DIR" \
METADATA_SUFFIX="$METADATA_SUFFIX" \
VERSION="$VERSION" \
SHA256="$SHA256" \
CHANNEL="stable" \
BASE_URL="homebrew" \
BINARY_NAME="$BINARY_NAME" \
bash "${HELPERS_DIR}/write-metadata.sh"
85 changes: 85 additions & 0 deletions .github/scripts/deployment-monitor/capture-linux-metadata.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# Captures version and sha256 metadata for Linux-based distribution channels (npm, snyk-images).
# Required env: SNYK_VERSION_DIR, METADATA_SUFFIX, BASE_URL, MANIFEST_MODE (stable|experimental)
# Optional env: SKIP_HASH_IF_NOT_ELF (set to 1 for npm)

if [[ -n "${CI:-}" ]]; then
set -euo pipefail
else
set -exuo pipefail
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HELPERS_DIR="${SCRIPT_DIR}/helpers"

: "${SNYK_VERSION_DIR:?SNYK_VERSION_DIR is required}"
: "${METADATA_SUFFIX:?METADATA_SUFFIX is required}"
: "${BASE_URL:?BASE_URL is required}"
: "${MANIFEST_MODE:?MANIFEST_MODE is required}"

VERSION="$(snyk --version | tr -d '\r\n')"
SNYK_PATH="$(command -v snyk)"
SHA256="$(shasum -a 256 "$SNYK_PATH" | awk '{print $1}')"
BINARY_NAME="$(bash "${HELPERS_DIR}/resolve-linux-binary-name.sh")"

case "$MANIFEST_MODE" in
stable)
MANIFEST_URL="https://downloads.snyk.io/cli/stable/sha256sums.txt.asc"
MISSING_HASH_MSG="Missing expected hash for ${BINARY_NAME} in manifest."
;;
experimental)
MANIFEST_URL="https://downloads.snyk.io/experimental/cli/v${VERSION}/sha256sums.txt.asc"
MISSING_HASH_MSG="Missing expected hash for ${BINARY_NAME} in experimental manifest."
;;
*)
echo "Unknown MANIFEST_MODE: ${MANIFEST_MODE} (expected stable or experimental)"
exit 1
;;
esac

HTTP_STATUS="$(
curl --retry 2 -sSL -w '%{http_code}' -o sha256sums.txt.asc "$MANIFEST_URL"
)"
if [ "$HTTP_STATUS" != "200" ]; then
echo "Failed to download manifest from ${MANIFEST_URL} (HTTP ${HTTP_STATUS})."
exit 1
fi
if [ ! -s sha256sums.txt.asc ]; then
echo "Downloaded manifest from ${MANIFEST_URL} is empty."
exit 1
fi
EXPECTED="$(
MANIFEST_FILE="sha256sums.txt.asc" BINARY_NAME="$BINARY_NAME" \
bash "${HELPERS_DIR}/lookup-hash-from-asc-manifest.sh"
)"

if [ -z "$EXPECTED" ]; then
echo "$MISSING_HASH_MSG"
exit 1
fi

if [ "${SKIP_HASH_IF_NOT_ELF:-}" = "1" ]; then
IS_ELF="$(go run -C "${SCRIPT_DIR}" ./cmd/is-elf-binary "$SNYK_PATH")"
if [ "$IS_ELF" != "1" ]; then
echo "Non-binary CLI detected for npm; skipping CDN hash comparison."
elif [ "$SHA256" != "$EXPECTED" ]; then
echo "Hash mismatch for ${BINARY_NAME} (${BASE_URL})."
echo "Expected: $EXPECTED"
echo "Actual: $SHA256"
exit 1
fi
elif [ "$SHA256" != "$EXPECTED" ]; then
echo "Hash mismatch for ${BINARY_NAME} (${BASE_URL})."
echo "Expected: $EXPECTED"
echo "Actual: $SHA256"
exit 1
fi

SNYK_VERSION_DIR="$SNYK_VERSION_DIR" \
METADATA_SUFFIX="$METADATA_SUFFIX" \
VERSION="$VERSION" \
SHA256="$SHA256" \
CHANNEL="stable" \
BASE_URL="$BASE_URL" \
BINARY_NAME="$BINARY_NAME" \
bash "${HELPERS_DIR}/write-metadata.sh"
165 changes: 165 additions & 0 deletions .github/scripts/deployment-monitor/cmd/compare-cdn-shasums/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package main

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)

type metadata struct {
Channel string `json:"channel"`
BaseURL string `json:"base_url"`
Binary string `json:"binary"`
SHA256 string `json:"sha256"`
}

type cdnKey struct {
channel string
binary string
}

type cdnEntry struct {
baseURL string
sha256 string
filename string
}

func main() {
snykVersionDir := os.Getenv("SNYK_VERSION_DIR")
if snykVersionDir == "" {
fmt.Fprintln(os.Stderr, "SNYK_VERSION_DIR is required")
os.Exit(1)
}

if !filepath.IsAbs(snykVersionDir) {
workspace := os.Getenv("GITHUB_WORKSPACE")
if workspace == "" {
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to resolve working directory: %v\n", err)
os.Exit(1)
}
workspace = cwd
}
snykVersionDir = filepath.Join(workspace, snykVersionDir)
}

if err := os.MkdirAll(snykVersionDir, 0o755); err != nil {
fmt.Fprintf(os.Stderr, "failed to create directory: %v\n", err)
os.Exit(1)
}

if err := os.Chdir(snykVersionDir); err != nil {
fmt.Fprintf(os.Stderr, "failed to change directory: %v\n", err)
os.Exit(1)
}

entries, err := os.ReadDir(".")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to read directory: %v\n", err)
os.Exit(1)
}

var metadataFiles []string
for _, entry := range entries {
name := entry.Name()
if strings.HasPrefix(name, "snyk-metadata-") && strings.HasSuffix(name, ".json") {
metadataFiles = append(metadataFiles, name)
}
}

if len(metadataFiles) == 0 {
fmt.Println("No metadata files found; skipping shasum comparison.")
return
}

cdnEntries := make(map[cdnKey][]cdnEntry)
for _, filename := range metadataFiles {
content, err := os.ReadFile(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to read %s: %v\n", filename, err)
os.Exit(1)
}

trimmed := strings.TrimSpace(string(content))
if trimmed == "" {
fmt.Printf("Metadata file %s is empty.\n", filename)
os.Exit(1)
}

var data metadata
if err := json.Unmarshal([]byte(trimmed), &data); err != nil {
snippet := strings.ReplaceAll(trimmed, "\n", "\\n")
if len(snippet) > 200 {
snippet = snippet[:200]
}
fmt.Printf("Invalid JSON in %s: %v\n", filename, err)
fmt.Printf("Content snippet: %s\n", snippet)
os.Exit(1)
}

if data.BaseURL != "static.snyk.io" && data.BaseURL != "downloads.snyk.io" {
continue
}

if data.SHA256 == "" {
continue
}

key := cdnKey{channel: data.Channel, binary: data.Binary}
if key.channel == "" {
key.channel = "unknown"
}
if key.binary == "" {
key.binary = "unknown"
}

cdnEntries[key] = append(cdnEntries[key], cdnEntry{
baseURL: data.BaseURL,
sha256: data.SHA256,
filename: filename,
})
}

if len(cdnEntries) == 0 {
fmt.Println("No CDN metadata entries found; skipping shasum comparison.")
return
}

keys := make([]cdnKey, 0, len(cdnEntries))
for key := range cdnEntries {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
if keys[i].channel != keys[j].channel {
return keys[i].channel < keys[j].channel
}
return keys[i].binary < keys[j].binary
})

failed := false
for _, key := range keys {
entriesForKey := cdnEntries[key]
uniqueSHAs := make(map[string]struct{})
for _, entry := range entriesForKey {
uniqueSHAs[entry.sha256] = struct{}{}
}

if len(uniqueSHAs) > 1 {
failed = true
fmt.Printf("❌ CDN shasum mismatch for %s (%s)\n", key.binary, key.channel)
for _, entry := range entriesForKey {
fmt.Printf(" %s: %s (%s)\n", entry.baseURL, entry.sha256, entry.filename)
}
}
}

if failed {
os.Exit(1)
}

fmt.Println("✅ CDN shasums are consistent within channels.")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"encoding/json"
"fmt"
"os"
"strings"
)

type releaseJSON struct {
Assets map[string]assetEntry `json:"assets"`
}

type assetEntry struct {
SHA256 string `json:"sha256"`
}

func main() {
name := os.Getenv("BINARY_NAME")
if name == "" {
fmt.Fprintln(os.Stderr, "BINARY_NAME environment variable is not set")
os.Exit(1)
}

releasePath := "release.json"
if len(os.Args) > 1 {
releasePath = os.Args[1]
}

content, err := os.ReadFile(releasePath)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read release file: %v\n", err)
os.Exit(1)
}

var data releaseJSON
if err := json.Unmarshal(content, &data); err != nil {
fmt.Fprintf(os.Stderr, "Failed to parse release JSON: %v\n", err)
os.Exit(1)
}

asset, ok := data.Assets[name]
if !ok || asset.SHA256 == "" {
fmt.Fprintln(os.Stderr, "Asset not found or SHA256 is empty")
os.Exit(1)
}

hash := strings.Fields(asset.SHA256)[0]
fmt.Println(hash)
}
42 changes: 42 additions & 0 deletions .github/scripts/deployment-monitor/cmd/is-elf-binary/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"errors"
"fmt"
"io"
"os"
)

const elfMagic = "\x7fELF"

func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Usage: is-elf-binary <file>")
os.Exit(1)
}

f, err := os.Open(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read file: %v\n", err)
os.Exit(1)
}
defer f.Close()

header := make([]byte, 4)
_, err = io.ReadFull(f, header)
if err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
fmt.Println("0")
return
}
fmt.Fprintf(os.Stderr, "Failed to read file: %v\n", err)
os.Exit(1)
}

if string(header) == elfMagic {
fmt.Println("1")
return
}

fmt.Println("0")
}
Loading
Loading