Token导航 LogoToken导航

Agent Skill · 研究检索

harness-writing(线束写作)

用于辅助文档、README、Markdown、说明文和内容稿件的整理与改写。它适合让 Agent 提炼结构、补齐章节、统一术语、检查链接或把零散材料整理成可读文档。使用时应保留项目已有事实、命令和路径,不要把未确认的信息写成确定结论;涉及对外文案时,还需要控制语气,避免过度营销或夸大能力。

harness-writing 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为调用外部服务;许可证为 MIT,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 3,280。

总安装 46,056周安装 1,885GitHub Stars 4,867下载 15,048核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:harness-writing(线束写作)
来源仓库:https://github.com/trailofbits/skills
仓库路径:skills/harness-writing
安装命令:
npx skills add https://github.com/trailofbits/skills --skill harness-writing
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/trailofbits/skills --skill harness-writing

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

跨语言和框架编写有效模糊测试的技术。

涵盖 C/C++、Rust、Go 和 Python 的基础知识,包括入口点签名、输入验证以及使用 FuzzedDataProvider 和任意 crate 进行结构化数据提取 包括从识别入口点到测试和迭代的分步指导,以及整数转换、复杂输入和多个操作的交错模糊测试的实用模式 为 libFuzzer、AFL++、cargo-fuzz 和 go-fuzz 提供特定于工具的工具实现,以及编译标志和运行指令 详细介绍要避免的反模式(全局状态、阻塞 I/O、内存泄漏、调用退出)、确定性要求以及低覆盖率或不可重现崩溃的故障排除策略

来源仓库内路径:skills/harness-writing

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/trailofbits/skills --skill harness-writing;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要调用外部服务,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

harness-writing 怎么安装?

在终端执行 npx skills add https://github.com/trailofbits/skills --skill harness-writing。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

harness-writing 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

harness-writing 需要什么权限?

权限级别是调用外部服务。该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

harness-writing 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

harness-writing 用的人多吗?

公开数据是累计安装 46,056 次,近一周安装 1,885 次,下载 15,048 次,GitHub 4,867 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

harness-writing 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
harness-writing

线束写作 · 本页

46,0564,867调用外部服务MIT通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Writing Fuzzing Harnesses

A fuzzing harness is the entrypoint function that receives random data from the fuzzer and routes it to your system under test (SUT). The quality of your harness directly determines which code paths get exercised and whether critical bugs are found. A poorly written harness can miss entire subsystems or produce non-reproducible crashes.

Overview

The harness is the bridge between the fuzzer's random byte generation and your application's API. It must parse raw bytes into meaningful inputs, call target functions, and handle edge cases gracefully. The most important part of any fuzzing setup is the harness—if written poorly, critical parts of your application may not be covered.

Key Concepts

ConceptDescription
HarnessFunction that receives fuzzer input and calls target code under test
SUTSystem Under Test—the code being fuzzed
Entry pointFunction signature required by the fuzzer (e.g., LLVMFuzzerTestOneInput)
FuzzedDataProviderHelper class for structured extraction of typed data from raw bytes
DeterminismProperty that ensures same input always produces same behavior
Interleaved fuzzingSingle harness that exercises multiple operations based on input

When to Apply

Apply this technique when:

  • Creating a new fuzz target for the first time
  • Fuzz campaign has low code coverage or isn't finding bugs
  • Crashes found during fuzzing are not reproducible
  • Target API requires complex or structured inputs
  • Multiple related functions should be tested together

Skip this technique when:

  • Using existing well-tested harnesses from your project
  • Tool provides automatic harness generation that meets your needs
  • Target already has comprehensive fuzzing infrastructure

Quick Reference

TaskPattern
Minimal C++ harnessextern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
Minimal Rust harness`fuzz_target!(
Size validationif (size < MIN_SIZE) return 0;
Cast to integersuint32_t val = *(uint32_t*)(data);
Use FuzzedDataProviderFuzzedDataProvider fuzzed_data(data, size);
Extract typed data (C++)auto val = fuzzed_data.ConsumeIntegral<uint32_t>();
Extract string (C++)auto str = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);

Step-by-Step

Step 1: Identify Entry Points

Find functions in your codebase that:

  • Accept external input (parsers, validators, protocol handlers)
  • Parse complex data formats (JSON, XML, binary protocols)
  • Perform security-critical operations (authentication, cryptography)
  • Have high cyclomatic complexity or many branches

Good targets are typically:

  • Protocol parsers
  • File format parsers
  • Serialization/deserialization functions
  • Input validation routines

Step 2: Write Minimal Harness

Start with the simplest possible harness that calls your target function:

C/C++:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    target_function(data, size);
    return 0;
}

Rust:

#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    target_function(data);
});

Step 3: Add Input Validation

Reject inputs that are too small or too large to be meaningful:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // Ensure minimum size for meaningful input
    if (size < MIN_INPUT_SIZE || size > MAX_INPUT_SIZE) {
        return 0;
    }
    target_function(data, size);
    return 0;
}

Rationale: The fuzzer generates random inputs of all sizes. Your harness must handle empty, tiny, huge, or malformed inputs without causing unexpected issues in the harness itself (crashes in the SUT are fine—that's what we're looking for).

Step 4: Structure the Input

For APIs that require typed data (integers, strings, etc.), use casting or helpers like FuzzedDataProvider:

Simple casting:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    if (size != 2 * sizeof(uint32_t)) {
        return 0;
    }

    uint32_t numerator = *(uint32_t*)(data);
    uint32_t denominator = *(uint32_t*)(data + sizeof(uint32_t));

    divide(numerator, denominator);
    return 0;
}

Using FuzzedDataProvider:

#include "FuzzedDataProvider.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    FuzzedDataProvider fuzzed_data(data, size);

    size_t allocation_size = fuzzed_data.ConsumeIntegral<size_t>();
    std::vector<char> str1 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
    std::vector<char> str2 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);

    concat(&str1[0], str1.size(), &str2[0], str2.size(), allocation_size);
    return 0;
}

Step 5: Test and Iterate

Run the fuzzer and monitor:

  • Code coverage (are all interesting paths reached?)
  • Executions per second (is it fast enough?)
  • Crash reproducibility (can you reproduce crashes with saved inputs?)

Iterate on the harness to improve these metrics.

Common Patterns

Pattern: Beyond Byte Arrays—Casting to Integers

Use Case: When target expects primitive types like integers or floats

Implementation:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // Ensure exactly 2 4-byte numbers
    if (size != 2 * sizeof(uint32_t)) {
        return 0;
    }

    // Split input into two integers
    uint32_t numerator = *(uint32_t*)(data);
    uint32_t denominator = *(uint32_t*)(data + sizeof(uint32_t));

    divide(numerator, denominator);
    return 0;
}

Rust equivalent:

fuzz_target!(|data: &[u8]| {
    if data.len() != 2 * std::mem::size_of::<i32>() {
        return;
    }

    let numerator = i32::from_ne_bytes([data[0], data[1], data[2], data[3]]);
    let denominator = i32::from_ne_bytes([data[4], data[5], data[6], data[7]]);

    divide(numerator, denominator);
});

Why it works: Any 8-byte input is valid. The fuzzer learns that inputs must be exactly 8 bytes, and every bit flip produces a new, potentially interesting input.

Pattern: FuzzedDataProvider for Complex Inputs

Use Case: When target requires multiple strings, integers, or variable-length data

Implementation:

#include "FuzzedDataProvider.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    FuzzedDataProvider fuzzed_data(data, size);

    // Extract different types of data
    size_t allocation_size = fuzzed_data.ConsumeIntegral<size_t>();

    // Consume variable-length strings with terminator
    std::vector<char> str1 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
    std::vector<char> str2 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);

    char* result = concat(&str1[0], str1.size(), &str2[0], str2.size(), allocation_size);
    if (result != NULL) {
        free(result);
    }

    return 0;
}

Why it helps: FuzzedDataProvider handles the complexity of extracting structured data from a byte stream. It's particularly useful for APIs that need multiple parameters of different types.

Pattern: Interleaved Fuzzing

Use Case: When multiple related operations should be tested in a single harness

Implementation:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    if (size < 1 + 2 * sizeof(int32_t)) {
        return 0;
    }

    // First byte selects operation
    uint8_t mode = data[0];

    // Next bytes are operands
    int32_t numbers[2];
    memcpy(numbers, data + 1, 2 * sizeof(int32_t));

    int32_t result = 0;
    switch (mode % 4) {
        case 0:
            result = add(numbers[0], numbers[1]);
            break;
        case 1:
            result = subtract(numbers[0], numbers[1]);
            break;
        case 2:
            result = multiply(numbers[0], numbers[1]);
            break;
        case 3:
            result = divide(numbers[0], numbers[1]);
            break;
    }

    // Prevent compiler from optimizing away the calls
    printf("%d", result);
    return 0;
}

Advantages:

  • Faster to write one harness than multiple individual harnesses
  • Single shared corpus means interesting inputs for one operation may be interesting for others
  • Can discover bugs in interactions between operations

When to use:

  • Operations share similar input types
  • Operations are logically related (e.g., arithmetic operations, CRUD operations)
  • Single corpus makes sense across all operations

Pattern: Structure-Aware Fuzzing with Arbitrary (Rust)

Use Case: When fuzzing Rust code that uses custom structs

Implementation:

use arbitrary::Arbitrary;

#[derive(Debug, Arbitrary)]
pub struct Name {
    data: String
}

impl Name {
    pub fn check_buf(&self) {
        let data = self.data.as_bytes();
        if data.len() > 0 && data[0] == b'a' {
            if data.len() > 1 && data[1] == b'b' {
                if data.len() > 2 && data[2] == b'c' {
                    process::abort();
                }
            }
        }
    }
}

Harness with arbitrary:

#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: your_project::Name| {
    data.check_buf();
});

Add to Cargo.toml:

[dependencies]
arbitrary = { version = "1", features = ["derive"] }

Why it helps: The arbitrary crate automatically handles deserialization of raw bytes into your Rust structs, reducing boilerplate and ensuring valid struct construction.

Limitation: The arbitrary crate doesn't offer reverse serialization, so you can't manually construct byte arrays that map to specific structs. This works best when starting from an empty corpus (fine for libFuzzer, problematic for AFL++).

Advanced Usage

Tips and Tricks

TipWhy It Helps
Start with parsersHigh bug density, clear entry points, easy to harness
Mock I/O operationsPrevents hangs from blocking I/O, enables determinism
Use FuzzedDataProviderSimplifies extraction of structured data from raw bytes
Reset global stateEnsures each iteration is independent and reproducible
Free resources in harnessPrevents memory exhaustion during long campaigns
Avoid logging in harnessLogging is slow—fuzzing needs 100s-1000s exec/sec
Test harness manually firstRun harness with known inputs before starting campaign
Check coverage earlyEnsure harness reaches expected code paths

Structure-Aware Fuzzing with Protocol Buffers

For highly structured input formats, consider using Protocol Buffers as an intermediate format with custom mutators:

// Define your input format in .proto file
// Use libprotobuf-mutator to generate valid mutations
// This ensures fuzzer mutates message contents, not the protobuf encoding itself

This approach is more setup but prevents the fuzzer from wasting time on unparseable inputs. See structure-aware fuzzing documentation for details.

Handling Non-Determinism

Problem: Random values or timing dependencies cause non-reproducible crashes.

Solutions:

  • Replace rand() with deterministic PRNG seeded from fuzzer input: uint32_t seed = fuzzed_data.ConsumeIntegral<uint32_t>(); srand(seed);
  • Mock system calls that return time, PIDs, or random data
  • Avoid reading from /dev/random or /dev/urandom

Resetting Global State

If your SUT uses global state (singletons, static variables), reset it between iterations:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // Reset global state before each iteration
    global_reset();

    target_function(data, size);

    // Clean up resources
    global_cleanup();
    return 0;
}

Rationale: Global state can cause crashes after N iterations rather than on a specific input, making bugs non-reproducible.

Practical Harness Rules

Follow these rules to ensure effective fuzzing harnesses:

RuleRationale
Handle all input sizesFuzzer generates empty, tiny, huge inputs—harness must handle gracefully
Never call exit()Calling exit() stops the fuzzer process. Use abort() in SUT if needed
Join all threadsEach iteration must run to completion before next iteration starts
Be fastAim for 100s-1000s executions/sec. Avoid logging, high complexity, excess memory
Maintain determinismSame input must always produce same behavior for reproducibility
Avoid global stateGlobal state reduces reproducibility—reset between iterations if unavoidable
Use narrow targetsDon't fuzz PNG and TCP in same harness—different formats need separate targets
Free resourcesPrevent memory leaks that cause resource exhaustion during long campaigns

Note: These guidelines apply not just to harness code, but to the entire SUT. If the SUT violates these rules, consider patching it (see the fuzzing obstacles technique).

Anti-Patterns

Anti-PatternProblemCorrect Approach
Global state without resetNon-deterministic crashesReset all globals at start of harness
Blocking I/O or network callsHangs fuzzer, wastes timeMock I/O, use in-memory buffers
Memory leaks in harnessResource exhaustion kills campaignFree all allocations before returning
Calling exit() in SUTStops entire fuzzing processUse abort() or return error codes
Heavy logging in harnessReduces exec/sec by orders of magnitudeDisable logging during fuzzing
Too many operations per iterationSlows down fuzzerKeep iterations fast and focused
Mixing unrelated input formatsCorpus entries not useful across formatsSeparate harnesses for different formats
Not validating input sizeHarness crashes on edge casesCheck size before accessing data

Tool-Specific Guidance

libFuzzer

Harness signature:

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // Your code here
    return 0;  // Non-zero return is reserved for future use
}

Compilation:

clang++ -fsanitize=fuzzer,address -g harness.cc -o fuzz_target

Integration tips:

  • Use FuzzedDataProvider.h for structured input extraction
  • Compile with -fsanitize=fuzzer to link the fuzzing runtime
  • Add sanitizers (-fsanitize=address,undefined) to detect more bugs
  • Use -g for better stack traces when crashes occur
  • libFuzzer can start with empty corpus—no seed inputs required

Running:

./fuzz_target corpus_dir/

Resources:

AFL++

AFL++ supports multiple harness styles. For best performance, use persistent mode:

Persistent mode harness:

#include <unistd.h>

int main(int argc, char **argv) {
    #ifdef __AFL_HAVE_MANUAL_CONTROL
        __AFL_INIT();
    #endif

    unsigned char buf[MAX_SIZE];

    while (__AFL_LOOP(10000)) {
        // Read input from stdin
        ssize_t len = read(0, buf, sizeof(buf));
        if (len <= 0) break;

        // Call target function
        target_function(buf, len);
    }

    return 0;
}

Compilation:

afl-clang-fast++ -g harness.cc -o fuzz_target

Integration tips:

  • Use persistent mode (__AFL_LOOP) for 10-100x speedup
  • Consider deferred initialization (__AFL_INIT()) to skip setup overhead
  • AFL++ requires at least one seed input in the corpus directory
  • Use AFL_USE_ASAN=1 or AFL_USE_UBSAN=1 for sanitizer builds

Running:

afl-fuzz -i seeds/ -o findings/ -- ./fuzz_target

cargo-fuzz (Rust)

Harness signature:

#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    // Your code here
});

With structured input (arbitrary crate):

#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: YourStruct| {
    data.check();
});

Creating harness:

cargo fuzz init
cargo fuzz add my_target

Integration tips:

  • Use arbitrary crate for automatic struct deserialization
  • cargo-fuzz wraps libFuzzer, so all libFuzzer features work
  • Compile with sanitizers automatically via cargo-fuzz
  • Harnesses go in fuzz/fuzz_targets/ directory

Running:

cargo +nightly fuzz run my_target

Resources:

go-fuzz

Harness signature:

// +build gofuzz

package mypackage

func Fuzz(data []byte) int {
    // Call target function
    target(data)

    // Return codes:
    // -1 if input is invalid
    //  0 if input is valid but not interesting
    //  1 if input is interesting (e.g., added new coverage)
    return 0
}

Building:

go-fuzz-build

Integration tips:

  • Return 1 for inputs that add coverage (optional—fuzzer can detect automatically)
  • Return -1 for invalid inputs to deprioritize similar mutations
  • go-fuzz handles persistence automatically

Running:

go-fuzz -bin=./mypackage-fuzz.zip -workdir=fuzz

Troubleshooting

IssueCauseSolution
Low executions/secHarness is too slow (logging, I/O, complexity)Profile harness, remove bottlenecks, mock I/O
No crashes foundCoverage not reaching buggy codeCheck coverage, improve harness to reach more paths
Non-reproducible crashesNon-determinism or global stateRemove randomness, reset globals between iterations
Fuzzer exits immediatelyHarness calls exit()Replace exit() with abort() or return error
Out of memory errorsMemory leaks in harness or SUTFree allocations, use leak sanitizer to find leaks
Crashes on empty inputHarness doesn't validate sizeAdd if (size < MIN_SIZE) return 0;
Corpus not growingInputs too constrained or format too strictUse FuzzedDataProvider or structure-aware fuzzing

Related Skills

Tools That Use This Technique

SkillHow It Applies
libfuzzerUses LLVMFuzzerTestOneInput harness signature with FuzzedDataProvider
aflppSupports persistent mode harnesses with __AFL_LOOP for performance
cargo-fuzzUses Rust-specific fuzz_target! macro with arbitrary crate integration
atherisPython harness takes bytes, calls Python functions
ossfuzzRequires harnesses in specific directory structure for cloud fuzzing

Related Techniques

SkillRelationship
coverage-analysisMeasure harness effectiveness—are you reaching target code?
address-sanitizerDetects bugs found by harness (buffer overflows, use-after-free)
fuzzing-dictionaryProvide tokens to help fuzzer pass format checks in harness
fuzzing-obstaclesPatch SUT when it violates harness rules (exit, non-determinism)

Resources

Key External Resources

Split Inputs in libFuzzer - Google Fuzzing Docs Explains techniques for handling multiple input parameters in a single fuzzing harness, including use of magic separators and FuzzedDataProvider.

Structure-Aware Fuzzing with Protocol Buffers Advanced technique using protobuf as intermediate format with custom mutators to ensure fuzzer mutates message contents rather than format encoding.

libFuzzer Documentation Official LLVM documentation covering harness requirements, best practices, and advanced features.

cargo-fuzz Book Comprehensive guide to writing Rust fuzzing harnesses with cargo-fuzz and the arbitrary crate.

Video Resources

平台分布

Claude Code

28.86%
按下载量换算4,343

OpenCode

26.27%
按下载量换算3,953

Gemini CLI

17.22%
按下载量换算2,591

Cursor

11.96%
按下载量换算1,800

Antigravity

7.4%
按下载量换算1,114

Codex

3.71%
按下载量换算558

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

调用外部服务

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

继续浏览同类 Skills