Reuse corpus
This commit is contained in:
parent
c96b893113
commit
588fa050cf
|
|
@ -60,6 +60,15 @@ runs:
|
|||
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 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"
|
||||
|
||||
- name: Setup Cairn CLI
|
||||
|
|
@ -86,11 +95,16 @@ runs:
|
|||
|
||||
# ── Start Cairn campaign ──
|
||||
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 \
|
||||
-server "${CAIRN_SERVER}" \
|
||||
-repo "${REPO}" \
|
||||
-owner "${OWNER}" \
|
||||
-name "fuzz-${SHORT_SHA}" \
|
||||
-name "${CAMPAIGN_NAME}" \
|
||||
-type fuzzing)
|
||||
CAMPAIGN_ID="${CAMPAIGN_OUTPUT#Campaign started: }"
|
||||
echo "Campaign ${CAMPAIGN_ID} started"
|
||||
|
|
@ -115,6 +129,18 @@ runs:
|
|||
echo "Target ${TARGET_NUM}: zig build ${BUILD_ARGS}"
|
||||
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 ──
|
||||
rm -rf zig-out
|
||||
zig build ${BUILD_ARGS}
|
||||
|
|
@ -142,11 +168,27 @@ runs:
|
|||
echo "Fuzz binary: ${FUZZ_BIN}"
|
||||
|
||||
# ── Seed corpus ──
|
||||
SEEDS="afl-seeds-${TARGET_NUM}"
|
||||
rm -rf "${SEEDS}"
|
||||
mkdir -p "${SEEDS}"
|
||||
|
||||
if [ -n "${CORPUS_DIR}" ] && [ -d "${CORPUS_DIR}" ]; then
|
||||
SEEDS="${CORPUS_DIR}"
|
||||
else
|
||||
SEEDS="afl-seeds"
|
||||
mkdir -p "${SEEDS}"
|
||||
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"
|
||||
fi
|
||||
|
||||
|
|
@ -192,11 +234,12 @@ runs:
|
|||
|
||||
echo "Uploading crash: ${CRASH_NAME}"
|
||||
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}"
|
||||
|
||||
if [ -n "${TARGET}" ]; then
|
||||
set -- "$@" -target "${TARGET}"
|
||||
if [ -n "${EFFECTIVE_TARGET}" ]; then
|
||||
set -- "$@" -target "${EFFECTIVE_TARGET}"
|
||||
fi
|
||||
if [ -n "${SIG}" ]; then
|
||||
set -- "$@" -signal "${SIG}"
|
||||
|
|
@ -216,9 +259,11 @@ runs:
|
|||
tar czf "corpus-${TARGET_NUM}.tar.gz" -C "${QUEUE_DIR}" .
|
||||
|
||||
set -- -server "${CAIRN_SERVER}" -repo "${REPO}" -owner "${OWNER}" \
|
||||
-commit "${COMMIT}" -type fuzz -file "corpus-${TARGET_NUM}.tar.gz"
|
||||
if [ -n "${TARGET}" ]; then
|
||||
set -- "$@" -target "${TARGET}"
|
||||
-commit "${COMMIT}" -campaign-id "${CAMPAIGN_ID}" -type fuzz \
|
||||
-kind corpus -track-key "${TRACK_KEY}" \
|
||||
-file "corpus-${TARGET_NUM}.tar.gz"
|
||||
if [ -n "${EFFECTIVE_TARGET}" ]; then
|
||||
set -- "$@" -target "${EFFECTIVE_TARGET}"
|
||||
fi
|
||||
|
||||
cairn upload "$@"
|
||||
|
|
|
|||
|
|
@ -133,6 +133,20 @@ func TestCLIUploadAndDownloadRoundTrip(t *testing.T) {
|
|||
serverURL, db, cleanup, _ := setupCLIServer(t, false)
|
||||
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")
|
||||
original := []byte("artifact bytes")
|
||||
if err := os.WriteFile(artifactFile, original, 0o644); err != nil {
|
||||
|
|
@ -144,10 +158,13 @@ func TestCLIUploadAndDownloadRoundTrip(t *testing.T) {
|
|||
"-repo", "demo",
|
||||
"-owner", "acme",
|
||||
"-commit", "abcdef1234567890",
|
||||
"-campaign-id", strconv.FormatUint(uint64(campaign.ID), 10),
|
||||
"-type", "fuzz",
|
||||
"-file", artifactFile,
|
||||
"-crash-message", "boom",
|
||||
"-signal", "11",
|
||||
"-kind", "crash",
|
||||
"-track-key", "track-123",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("cmdUpload failed: %v", err)
|
||||
|
|
@ -165,6 +182,15 @@ func TestCLIUploadAndDownloadRoundTrip(t *testing.T) {
|
|||
if got := md["signal"]; got != "11" {
|
||||
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")
|
||||
if err := cmdDownload([]string{"-server", serverURL, "-id", strconv.FormatUint(uint64(a.ID), 10), "-o", outFile}); err != nil {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var version = "dev"
|
||||
|
|
@ -69,10 +70,13 @@ Upload flags:
|
|||
-repo NAME Repository name (required)
|
||||
-owner OWNER Repository owner (required)
|
||||
-commit SHA Commit SHA (required)
|
||||
-campaign-id ID Campaign ID (optional)
|
||||
-type TYPE Artifact type: coredump, fuzz, sanitizer, simulation (required)
|
||||
-file PATH Path to artifact file (required)
|
||||
-crash-message MSG Crash message (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)
|
||||
-seed VALUE Simulation seed for reproducibility (optional, stored in metadata)
|
||||
-target NAME Target name/platform (optional, stored in metadata)
|
||||
|
|
@ -85,10 +89,13 @@ func cmdUpload(args []string) error {
|
|||
repo string
|
||||
owner string
|
||||
commitSHA string
|
||||
campaignID string
|
||||
artifactType string
|
||||
filePath string
|
||||
crashMessage string
|
||||
stackTrace string
|
||||
kind string
|
||||
trackKey string
|
||||
signal string
|
||||
seed string
|
||||
target string
|
||||
|
|
@ -108,6 +115,9 @@ func cmdUpload(args []string) error {
|
|||
case "-commit":
|
||||
i++
|
||||
commitSHA = args[i]
|
||||
case "-campaign-id":
|
||||
i++
|
||||
campaignID = args[i]
|
||||
case "-type":
|
||||
i++
|
||||
artifactType = args[i]
|
||||
|
|
@ -120,6 +130,12 @@ func cmdUpload(args []string) error {
|
|||
case "-stack-trace":
|
||||
i++
|
||||
stackTrace = args[i]
|
||||
case "-kind":
|
||||
i++
|
||||
kind = args[i]
|
||||
case "-track-key":
|
||||
i++
|
||||
trackKey = args[i]
|
||||
case "-signal":
|
||||
i++
|
||||
signal = args[i]
|
||||
|
|
@ -144,6 +160,13 @@ func cmdUpload(args []string) error {
|
|||
"commit_sha": commitSHA,
|
||||
"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 != "" {
|
||||
meta["crash_message"] = crashMessage
|
||||
}
|
||||
|
|
@ -151,6 +174,12 @@ func cmdUpload(args []string) error {
|
|||
meta["stack_trace"] = stackTrace
|
||||
}
|
||||
md := map[string]any{}
|
||||
if kind != "" {
|
||||
md["kind"] = kind
|
||||
}
|
||||
if trackKey != "" {
|
||||
md["track_key"] = trackKey
|
||||
}
|
||||
if signal != "" {
|
||||
md["signal"] = signal
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ type IngestRequest struct {
|
|||
Repository string `json:"repository"`
|
||||
Owner string `json:"owner"`
|
||||
CommitSHA string `json:"commit_sha"`
|
||||
CampaignID *uint `json:"campaign_id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
CrashMessage string `json:"crash_message,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{
|
||||
RepositoryID: repo.ID,
|
||||
CommitID: commit.ID,
|
||||
CampaignID: req.CampaignID,
|
||||
Type: req.Type,
|
||||
BlobKey: blobKey,
|
||||
BlobSize: header.Size,
|
||||
|
|
|
|||
Loading…
Reference in New Issue