Reuse corpus
This commit is contained in:
parent
c96b893113
commit
588fa050cf
|
|
@ -60,6 +60,15 @@ runs:
|
||||||
set -eu
|
set -eu
|
||||||
command -v zig >/dev/null 2>&1 || { echo "ERROR: zig not found in PATH. Install Zig before using this action (e.g. https://codeberg.org/mlugg/setup-zig@v2)."; exit 1; }
|
command -v zig >/dev/null 2>&1 || { echo "ERROR: zig not found in PATH. Install Zig before using this action (e.g. https://codeberg.org/mlugg/setup-zig@v2)."; exit 1; }
|
||||||
command -v afl-cc >/dev/null 2>&1 || { echo "ERROR: afl-cc not found in PATH after setup-afl step."; exit 1; }
|
command -v afl-cc >/dev/null 2>&1 || { echo "ERROR: afl-cc not found in PATH after setup-afl step."; exit 1; }
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq jq >/dev/null 2>&1
|
||||||
|
else
|
||||||
|
echo "ERROR: jq not found and apt-get unavailable"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
echo "zig $(zig version), afl-cc found"
|
echo "zig $(zig version), afl-cc found"
|
||||||
|
|
||||||
- name: Setup Cairn CLI
|
- name: Setup Cairn CLI
|
||||||
|
|
@ -86,11 +95,16 @@ runs:
|
||||||
|
|
||||||
# ── Start Cairn campaign ──
|
# ── Start Cairn campaign ──
|
||||||
SHORT_SHA=$(printf '%.8s' "${COMMIT}")
|
SHORT_SHA=$(printf '%.8s' "${COMMIT}")
|
||||||
|
if [ -n "${TARGET}" ]; then
|
||||||
|
CAMPAIGN_NAME="fuzz-${TARGET}"
|
||||||
|
else
|
||||||
|
CAMPAIGN_NAME="fuzz-${SHORT_SHA}"
|
||||||
|
fi
|
||||||
CAMPAIGN_OUTPUT=$(cairn campaign start \
|
CAMPAIGN_OUTPUT=$(cairn campaign start \
|
||||||
-server "${CAIRN_SERVER}" \
|
-server "${CAIRN_SERVER}" \
|
||||||
-repo "${REPO}" \
|
-repo "${REPO}" \
|
||||||
-owner "${OWNER}" \
|
-owner "${OWNER}" \
|
||||||
-name "fuzz-${SHORT_SHA}" \
|
-name "${CAMPAIGN_NAME}" \
|
||||||
-type fuzzing)
|
-type fuzzing)
|
||||||
CAMPAIGN_ID="${CAMPAIGN_OUTPUT#Campaign started: }"
|
CAMPAIGN_ID="${CAMPAIGN_OUTPUT#Campaign started: }"
|
||||||
echo "Campaign ${CAMPAIGN_ID} started"
|
echo "Campaign ${CAMPAIGN_ID} started"
|
||||||
|
|
@ -115,6 +129,18 @@ runs:
|
||||||
echo "Target ${TARGET_NUM}: zig build ${BUILD_ARGS}"
|
echo "Target ${TARGET_NUM}: zig build ${BUILD_ARGS}"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Special-case Zig fuzz targets: if build args contain -Dfuzz-target=<name>,
|
||||||
|
# use that as the effective Cairn target for metadata/track keying.
|
||||||
|
LINE_FUZZ_TARGET=$(printf '%s' "${BUILD_ARGS}" | sed -n 's/.*-Dfuzz-target=\([^[:space:]]*\).*/\1/p')
|
||||||
|
EFFECTIVE_TARGET="${TARGET}"
|
||||||
|
if [ -n "${LINE_FUZZ_TARGET}" ]; then
|
||||||
|
EFFECTIVE_TARGET="${LINE_FUZZ_TARGET}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
TRACK_SOURCE="${OWNER}/${REPO}|${EFFECTIVE_TARGET}|${BUILD_ARGS}"
|
||||||
|
TRACK_KEY=$(printf '%s' "${TRACK_SOURCE}" | sha256sum | awk '{print $1}')
|
||||||
|
echo "Track key: ${TRACK_KEY}"
|
||||||
|
|
||||||
# ── Build ──
|
# ── Build ──
|
||||||
rm -rf zig-out
|
rm -rf zig-out
|
||||||
zig build ${BUILD_ARGS}
|
zig build ${BUILD_ARGS}
|
||||||
|
|
@ -142,11 +168,27 @@ runs:
|
||||||
echo "Fuzz binary: ${FUZZ_BIN}"
|
echo "Fuzz binary: ${FUZZ_BIN}"
|
||||||
|
|
||||||
# ── Seed corpus ──
|
# ── Seed corpus ──
|
||||||
if [ -n "${CORPUS_DIR}" ] && [ -d "${CORPUS_DIR}" ]; then
|
SEEDS="afl-seeds-${TARGET_NUM}"
|
||||||
SEEDS="${CORPUS_DIR}"
|
rm -rf "${SEEDS}"
|
||||||
else
|
|
||||||
SEEDS="afl-seeds"
|
|
||||||
mkdir -p "${SEEDS}"
|
mkdir -p "${SEEDS}"
|
||||||
|
|
||||||
|
if [ -n "${CORPUS_DIR}" ] && [ -d "${CORPUS_DIR}" ]; then
|
||||||
|
cp -a "${CORPUS_DIR}/." "${SEEDS}/" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
LATEST_CORPUS_ID=$(curl -fsSL "${CAIRN_SERVER}/api/v1/artifacts?type=fuzz&limit=200" \
|
||||||
|
| jq -r --arg track "${TRACK_KEY}" --arg repo "${REPO}" \
|
||||||
|
'.artifacts[]? | select(.repo_name == $repo and (.metadata.kind // "") == "corpus" and (.metadata.track_key // "") == $track) | .id' \
|
||||||
|
| head -n1)
|
||||||
|
if [ -n "${LATEST_CORPUS_ID}" ] && [ "${LATEST_CORPUS_ID}" != "null" ]; then
|
||||||
|
echo "Downloading prior corpus artifact: ${LATEST_CORPUS_ID}"
|
||||||
|
if cairn download -server "${CAIRN_SERVER}" -id "${LATEST_CORPUS_ID}" -o "prior-corpus-${TARGET_NUM}.tar.gz"; then
|
||||||
|
tar xzf "prior-corpus-${TARGET_NUM}.tar.gz" -C "${SEEDS}" || true
|
||||||
|
rm -f "prior-corpus-${TARGET_NUM}.tar.gz"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(find "${SEEDS}" -maxdepth 1 -type f | wc -l)" -eq 0 ]; then
|
||||||
printf 'A' > "${SEEDS}/seed-0"
|
printf 'A' > "${SEEDS}/seed-0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -192,11 +234,12 @@ runs:
|
||||||
|
|
||||||
echo "Uploading crash: ${CRASH_NAME}"
|
echo "Uploading crash: ${CRASH_NAME}"
|
||||||
set -- -server "${CAIRN_SERVER}" -repo "${REPO}" -owner "${OWNER}" \
|
set -- -server "${CAIRN_SERVER}" -repo "${REPO}" -owner "${OWNER}" \
|
||||||
-commit "${COMMIT}" -type fuzz -file "${crash_file}" \
|
-commit "${COMMIT}" -campaign-id "${CAMPAIGN_ID}" -type fuzz -file "${crash_file}" \
|
||||||
|
-kind crash -track-key "${TRACK_KEY}" \
|
||||||
-crash-message "AFL++ crash (${BUILD_ARGS}): ${CRASH_NAME}"
|
-crash-message "AFL++ crash (${BUILD_ARGS}): ${CRASH_NAME}"
|
||||||
|
|
||||||
if [ -n "${TARGET}" ]; then
|
if [ -n "${EFFECTIVE_TARGET}" ]; then
|
||||||
set -- "$@" -target "${TARGET}"
|
set -- "$@" -target "${EFFECTIVE_TARGET}"
|
||||||
fi
|
fi
|
||||||
if [ -n "${SIG}" ]; then
|
if [ -n "${SIG}" ]; then
|
||||||
set -- "$@" -signal "${SIG}"
|
set -- "$@" -signal "${SIG}"
|
||||||
|
|
@ -216,9 +259,11 @@ runs:
|
||||||
tar czf "corpus-${TARGET_NUM}.tar.gz" -C "${QUEUE_DIR}" .
|
tar czf "corpus-${TARGET_NUM}.tar.gz" -C "${QUEUE_DIR}" .
|
||||||
|
|
||||||
set -- -server "${CAIRN_SERVER}" -repo "${REPO}" -owner "${OWNER}" \
|
set -- -server "${CAIRN_SERVER}" -repo "${REPO}" -owner "${OWNER}" \
|
||||||
-commit "${COMMIT}" -type fuzz -file "corpus-${TARGET_NUM}.tar.gz"
|
-commit "${COMMIT}" -campaign-id "${CAMPAIGN_ID}" -type fuzz \
|
||||||
if [ -n "${TARGET}" ]; then
|
-kind corpus -track-key "${TRACK_KEY}" \
|
||||||
set -- "$@" -target "${TARGET}"
|
-file "corpus-${TARGET_NUM}.tar.gz"
|
||||||
|
if [ -n "${EFFECTIVE_TARGET}" ]; then
|
||||||
|
set -- "$@" -target "${EFFECTIVE_TARGET}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cairn upload "$@"
|
cairn upload "$@"
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,20 @@ func TestCLIUploadAndDownloadRoundTrip(t *testing.T) {
|
||||||
serverURL, db, cleanup, _ := setupCLIServer(t, false)
|
serverURL, db, cleanup, _ := setupCLIServer(t, false)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
|
if err := cmdCampaign("start", []string{
|
||||||
|
"-server", serverURL,
|
||||||
|
"-repo", "demo",
|
||||||
|
"-owner", "acme",
|
||||||
|
"-name", "seed-campaign",
|
||||||
|
"-type", "fuzz",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("cmdCampaign start failed: %v", err)
|
||||||
|
}
|
||||||
|
var campaign models.Campaign
|
||||||
|
if err := db.First(&campaign).Error; err != nil {
|
||||||
|
t.Fatalf("querying campaign: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
artifactFile := filepath.Join(t.TempDir(), "artifact.bin")
|
artifactFile := filepath.Join(t.TempDir(), "artifact.bin")
|
||||||
original := []byte("artifact bytes")
|
original := []byte("artifact bytes")
|
||||||
if err := os.WriteFile(artifactFile, original, 0o644); err != nil {
|
if err := os.WriteFile(artifactFile, original, 0o644); err != nil {
|
||||||
|
|
@ -144,10 +158,13 @@ func TestCLIUploadAndDownloadRoundTrip(t *testing.T) {
|
||||||
"-repo", "demo",
|
"-repo", "demo",
|
||||||
"-owner", "acme",
|
"-owner", "acme",
|
||||||
"-commit", "abcdef1234567890",
|
"-commit", "abcdef1234567890",
|
||||||
|
"-campaign-id", strconv.FormatUint(uint64(campaign.ID), 10),
|
||||||
"-type", "fuzz",
|
"-type", "fuzz",
|
||||||
"-file", artifactFile,
|
"-file", artifactFile,
|
||||||
"-crash-message", "boom",
|
"-crash-message", "boom",
|
||||||
"-signal", "11",
|
"-signal", "11",
|
||||||
|
"-kind", "crash",
|
||||||
|
"-track-key", "track-123",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("cmdUpload failed: %v", err)
|
t.Fatalf("cmdUpload failed: %v", err)
|
||||||
|
|
@ -165,6 +182,15 @@ func TestCLIUploadAndDownloadRoundTrip(t *testing.T) {
|
||||||
if got := md["signal"]; got != "11" {
|
if got := md["signal"]; got != "11" {
|
||||||
t.Fatalf("expected metadata.signal=11, got %#v", got)
|
t.Fatalf("expected metadata.signal=11, got %#v", got)
|
||||||
}
|
}
|
||||||
|
if got := md["kind"]; got != "crash" {
|
||||||
|
t.Fatalf("expected metadata.kind=crash, got %#v", got)
|
||||||
|
}
|
||||||
|
if got := md["track_key"]; got != "track-123" {
|
||||||
|
t.Fatalf("expected metadata.track_key=track-123, got %#v", got)
|
||||||
|
}
|
||||||
|
if a.CampaignID == nil || *a.CampaignID != campaign.ID {
|
||||||
|
t.Fatalf("expected campaign_id=%d, got %#v", campaign.ID, a.CampaignID)
|
||||||
|
}
|
||||||
|
|
||||||
outFile := filepath.Join(t.TempDir(), "downloaded.bin")
|
outFile := filepath.Join(t.TempDir(), "downloaded.bin")
|
||||||
if err := cmdDownload([]string{"-server", serverURL, "-id", strconv.FormatUint(uint64(a.ID), 10), "-o", outFile}); err != nil {
|
if err := cmdDownload([]string{"-server", serverURL, "-id", strconv.FormatUint(uint64(a.ID), 10), "-o", outFile}); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "dev"
|
var version = "dev"
|
||||||
|
|
@ -69,10 +70,13 @@ Upload flags:
|
||||||
-repo NAME Repository name (required)
|
-repo NAME Repository name (required)
|
||||||
-owner OWNER Repository owner (required)
|
-owner OWNER Repository owner (required)
|
||||||
-commit SHA Commit SHA (required)
|
-commit SHA Commit SHA (required)
|
||||||
|
-campaign-id ID Campaign ID (optional)
|
||||||
-type TYPE Artifact type: coredump, fuzz, sanitizer, simulation (required)
|
-type TYPE Artifact type: coredump, fuzz, sanitizer, simulation (required)
|
||||||
-file PATH Path to artifact file (required)
|
-file PATH Path to artifact file (required)
|
||||||
-crash-message MSG Crash message (optional)
|
-crash-message MSG Crash message (optional)
|
||||||
-stack-trace TRACE Stack trace text (optional)
|
-stack-trace TRACE Stack trace text (optional)
|
||||||
|
-kind VALUE Artifact kind label (optional, stored in metadata)
|
||||||
|
-track-key VALUE Fuzz track key (optional, stored in metadata)
|
||||||
-signal VALUE Crash signal number/name (optional, stored in metadata)
|
-signal VALUE Crash signal number/name (optional, stored in metadata)
|
||||||
-seed VALUE Simulation seed for reproducibility (optional, stored in metadata)
|
-seed VALUE Simulation seed for reproducibility (optional, stored in metadata)
|
||||||
-target NAME Target name/platform (optional, stored in metadata)
|
-target NAME Target name/platform (optional, stored in metadata)
|
||||||
|
|
@ -85,10 +89,13 @@ func cmdUpload(args []string) error {
|
||||||
repo string
|
repo string
|
||||||
owner string
|
owner string
|
||||||
commitSHA string
|
commitSHA string
|
||||||
|
campaignID string
|
||||||
artifactType string
|
artifactType string
|
||||||
filePath string
|
filePath string
|
||||||
crashMessage string
|
crashMessage string
|
||||||
stackTrace string
|
stackTrace string
|
||||||
|
kind string
|
||||||
|
trackKey string
|
||||||
signal string
|
signal string
|
||||||
seed string
|
seed string
|
||||||
target string
|
target string
|
||||||
|
|
@ -108,6 +115,9 @@ func cmdUpload(args []string) error {
|
||||||
case "-commit":
|
case "-commit":
|
||||||
i++
|
i++
|
||||||
commitSHA = args[i]
|
commitSHA = args[i]
|
||||||
|
case "-campaign-id":
|
||||||
|
i++
|
||||||
|
campaignID = args[i]
|
||||||
case "-type":
|
case "-type":
|
||||||
i++
|
i++
|
||||||
artifactType = args[i]
|
artifactType = args[i]
|
||||||
|
|
@ -120,6 +130,12 @@ func cmdUpload(args []string) error {
|
||||||
case "-stack-trace":
|
case "-stack-trace":
|
||||||
i++
|
i++
|
||||||
stackTrace = args[i]
|
stackTrace = args[i]
|
||||||
|
case "-kind":
|
||||||
|
i++
|
||||||
|
kind = args[i]
|
||||||
|
case "-track-key":
|
||||||
|
i++
|
||||||
|
trackKey = args[i]
|
||||||
case "-signal":
|
case "-signal":
|
||||||
i++
|
i++
|
||||||
signal = args[i]
|
signal = args[i]
|
||||||
|
|
@ -144,6 +160,13 @@ func cmdUpload(args []string) error {
|
||||||
"commit_sha": commitSHA,
|
"commit_sha": commitSHA,
|
||||||
"type": artifactType,
|
"type": artifactType,
|
||||||
}
|
}
|
||||||
|
if campaignID != "" {
|
||||||
|
id, err := strconv.ParseUint(campaignID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid campaign id: %w", err)
|
||||||
|
}
|
||||||
|
meta["campaign_id"] = id
|
||||||
|
}
|
||||||
if crashMessage != "" {
|
if crashMessage != "" {
|
||||||
meta["crash_message"] = crashMessage
|
meta["crash_message"] = crashMessage
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +174,12 @@ func cmdUpload(args []string) error {
|
||||||
meta["stack_trace"] = stackTrace
|
meta["stack_trace"] = stackTrace
|
||||||
}
|
}
|
||||||
md := map[string]any{}
|
md := map[string]any{}
|
||||||
|
if kind != "" {
|
||||||
|
md["kind"] = kind
|
||||||
|
}
|
||||||
|
if trackKey != "" {
|
||||||
|
md["track_key"] = trackKey
|
||||||
|
}
|
||||||
if signal != "" {
|
if signal != "" {
|
||||||
md["signal"] = signal
|
md["signal"] = signal
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ type IngestRequest struct {
|
||||||
Repository string `json:"repository"`
|
Repository string `json:"repository"`
|
||||||
Owner string `json:"owner"`
|
Owner string `json:"owner"`
|
||||||
CommitSHA string `json:"commit_sha"`
|
CommitSHA string `json:"commit_sha"`
|
||||||
|
CampaignID *uint `json:"campaign_id,omitempty"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
CrashMessage string `json:"crash_message,omitempty"`
|
CrashMessage string `json:"crash_message,omitempty"`
|
||||||
StackTrace string `json:"stack_trace,omitempty"`
|
StackTrace string `json:"stack_trace,omitempty"`
|
||||||
|
|
@ -93,6 +94,7 @@ func (h *IngestHandler) Create(c *gin.Context) {
|
||||||
artifact, err := models.CreateArtifact(ctx, h.DB, models.CreateArtifactParams{
|
artifact, err := models.CreateArtifact(ctx, h.DB, models.CreateArtifactParams{
|
||||||
RepositoryID: repo.ID,
|
RepositoryID: repo.ID,
|
||||||
CommitID: commit.ID,
|
CommitID: commit.ID,
|
||||||
|
CampaignID: req.CampaignID,
|
||||||
Type: req.Type,
|
Type: req.Type,
|
||||||
BlobKey: blobKey,
|
BlobKey: blobKey,
|
||||||
BlobSize: header.Size,
|
BlobSize: header.Size,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue