@mrciphersmith/keryx 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +4634 -2445
- package/dist/core.js +66 -10
- package/package.json +1 -1
- package/src/gdskills/bundled/install-manifest.json +349 -2
- package/src/gdskills/bundled/rules/core/model-selection.mdc +18 -0
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-jev-contract/SKILL.md +193 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +81 -21
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +4 -4
- package/src/gdskills/bundled/stacks/c-cpp/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/c-cpp/governance/eval.json +1777 -0
- package/src/gdskills/bundled/stacks/c-cpp/governance/scout.json +31 -0
- package/src/gdskills/bundled/stacks/c-cpp/pack.json +42 -0
- package/src/gdskills/bundled/stacks/c-cpp/rules/coding-style.mdc +80 -0
- package/src/gdskills/bundled/stacks/c-cpp/rules/patterns.mdc +87 -0
- package/src/gdskills/bundled/stacks/c-cpp/rules/security.mdc +90 -0
- package/src/gdskills/bundled/stacks/c-cpp/rules/testing.mdc +83 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-build-fix/SKILL.md +153 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-build-fix/evals.json +74 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-code-review/SKILL.md +132 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-code-review/evals.json +73 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-implementation/SKILL.md +151 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-implementation/evals.json +74 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-testing/SKILL.md +152 -0
- package/src/gdskills/bundled/stacks/c-cpp/skills/c-cpp-testing/evals.json +74 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/governance/eval.json +1295 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/governance/scout.json +26 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/pack.json +41 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/rules/patterns.mdc +77 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/rules/security.mdc +144 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-build-fix/SKILL.md +121 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-build-fix/evals.json +73 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-code-review/SKILL.md +139 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-code-review/evals.json +73 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-implementation/SKILL.md +147 -0
- package/src/gdskills/bundled/stacks/ci-github-gitlab/skills/ci-pipeline-implementation/evals.json +74 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/governance/eval.json +865 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/governance/scout.json +16 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/pack.json +46 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/coding-style.mdc +74 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/patterns.mdc +81 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/security.mdc +146 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/rules/testing.mdc +61 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-build-fix/SKILL.md +151 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-build-fix/evals.json +74 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-review/SKILL.md +135 -0
- package/src/gdskills/bundled/stacks/docker-k8s-terraform/skills/docker-k8s-terraform-review/evals.json +76 -0
- package/src/gdskills/bundled/stacks/php-laravel/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/php-laravel/governance/eval.json +1829 -0
- package/src/gdskills/bundled/stacks/php-laravel/governance/scout.json +33 -0
- package/src/gdskills/bundled/stacks/php-laravel/pack.json +41 -0
- package/src/gdskills/bundled/stacks/php-laravel/rules/coding-style.mdc +82 -0
- package/src/gdskills/bundled/stacks/php-laravel/rules/patterns.mdc +80 -0
- package/src/gdskills/bundled/stacks/php-laravel/rules/security.mdc +80 -0
- package/src/gdskills/bundled/stacks/php-laravel/rules/testing.mdc +82 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-build-fix/SKILL.md +143 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-build-fix/evals.json +74 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-code-review/SKILL.md +126 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-code-review/evals.json +76 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-implementation/SKILL.md +140 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-implementation/evals.json +75 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-testing/SKILL.md +124 -0
- package/src/gdskills/bundled/stacks/php-laravel/skills/php-laravel-testing/evals.json +74 -0
- package/src/gdskills/bundled/stacks/ruby-rails/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/ruby-rails/governance/eval.json +1673 -0
- package/src/gdskills/bundled/stacks/ruby-rails/governance/scout.json +33 -0
- package/src/gdskills/bundled/stacks/ruby-rails/pack.json +42 -0
- package/src/gdskills/bundled/stacks/ruby-rails/rules/coding-style.mdc +69 -0
- package/src/gdskills/bundled/stacks/ruby-rails/rules/patterns.mdc +93 -0
- package/src/gdskills/bundled/stacks/ruby-rails/rules/security.mdc +90 -0
- package/src/gdskills/bundled/stacks/ruby-rails/rules/testing.mdc +89 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-build-fix/SKILL.md +143 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-build-fix/evals.json +73 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-code-review/SKILL.md +134 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-code-review/evals.json +71 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-implementation/SKILL.md +141 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-implementation/evals.json +72 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-testing/SKILL.md +125 -0
- package/src/gdskills/bundled/stacks/ruby-rails/skills/ruby-rails-testing/evals.json +72 -0
- package/src/gdskills/bundled/stacks/sql-db/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/sql-db/governance/eval.json +1829 -0
- package/src/gdskills/bundled/stacks/sql-db/governance/scout.json +30 -0
- package/src/gdskills/bundled/stacks/sql-db/pack.json +40 -0
- package/src/gdskills/bundled/stacks/sql-db/rules/coding-style.mdc +69 -0
- package/src/gdskills/bundled/stacks/sql-db/rules/patterns.mdc +134 -0
- package/src/gdskills/bundled/stacks/sql-db/rules/security.mdc +74 -0
- package/src/gdskills/bundled/stacks/sql-db/rules/testing.mdc +83 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-build-fix/SKILL.md +147 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-build-fix/evals.json +72 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-code-review/SKILL.md +132 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-code-review/evals.json +73 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-implementation/SKILL.md +153 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-implementation/evals.json +77 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-testing/SKILL.md +129 -0
- package/src/gdskills/bundled/stacks/sql-db/skills/sql-db-testing/evals.json +73 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Use when implementing or extending a feature in C or modern C++ (17/20/23) -- ownership and RAII, smart pointer choice, move semantics, std::span, manual malloc/free lifetime in C, and choosing safe standard-library APIs over unsafe ones.",
|
|
4
|
+
"decision": "create",
|
|
5
|
+
"topMatch": "python/python-implementation",
|
|
6
|
+
"recordedAt": "2026-09-25T15:29:36.971Z",
|
|
7
|
+
"skillName": "c-cpp-implementation"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"query": "Use when reviewing a C or C++ change for memory-safety and undefined-behavior risks -- use-after-free, double-free, buffer overflows, dangling references, iterator invalidation, unchecked allocations, signed overflow, and unsynchronized shared state. Read-only, no edits.",
|
|
11
|
+
"decision": "create",
|
|
12
|
+
"topMatch": "c-cpp/c-cpp-build-fix",
|
|
13
|
+
"recordedAt": "2026-09-25T15:29:43.782Z",
|
|
14
|
+
"skillName": "c-cpp-code-review"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"query": "Use when a C/C++ build fails -- CMake configure/link errors, missing headers, template/generic-instantiation errors, ABI/linker mismatches -- or when a sanitizer (ASan/UBSan/TSan) or ctest run reports a memory-safety, UB, or race failure, with the smallest root-cause fix.",
|
|
18
|
+
"decision": "create",
|
|
19
|
+
"topMatch": "go/go-build-fix",
|
|
20
|
+
"recordedAt": "2026-09-25T15:29:46.104Z",
|
|
21
|
+
"skillName": "c-cpp-build-fix"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"query": "Use when a C or C++ test suite needs writing, extending, or fixing -- GoogleTest TEST/TEST_F/TEST_P, death tests, fixture setup/teardown, and verifying a memory-safety or concurrency fix under AddressSanitizer/UndefinedBehaviorSanitizer/ThreadSanitizer.",
|
|
25
|
+
"decision": "fork",
|
|
26
|
+
"topMatch": "c-cpp/c-cpp-build-fix",
|
|
27
|
+
"recordedAt": "2026-09-25T15:30:02.567Z",
|
|
28
|
+
"skillName": "c-cpp-testing",
|
|
29
|
+
"justification": "Nearest match c-cpp-build-fix (0.39) shares this pack's own sanitizer vocabulary (ASan/UBSan/TSan) because both skills verify fixes under sanitizers, but they are distinct capabilities: c-cpp-testing writes/extends/fixes GoogleTest test files (TEST_F fixtures, TEST_P, death tests) and never touches source under test, while c-cpp-build-fix diagnoses and fixes compile/link/CMake failures and root-causes a sanitizer-reported bug in production source, never writing new tests. No other matched skill (go-testing, php-laravel-testing, react-testing, ruby-rails-testing, all ~0.21) covers GoogleTest/C++ test idiom or sanitizer-backed test verification at all, so this is a genuine fork of a sibling skill within the same new pack, not a substitute for an existing one."
|
|
30
|
+
}
|
|
31
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "c-cpp",
|
|
3
|
+
"family": "language",
|
|
4
|
+
"modules": ["c-cpp-rules", "c-cpp-skills"],
|
|
5
|
+
"detectionMarkers": ["c-cpp"],
|
|
6
|
+
"provenance": {
|
|
7
|
+
"origin": "authored",
|
|
8
|
+
"sourceRef": "flow 337, Wave 4 batch 5"
|
|
9
|
+
},
|
|
10
|
+
"stability": "experimental",
|
|
11
|
+
"skills": {
|
|
12
|
+
"implement": ["c-cpp-implementation"],
|
|
13
|
+
"test": ["c-cpp-testing"],
|
|
14
|
+
"review": ["c-cpp-code-review"],
|
|
15
|
+
"build-fix": ["c-cpp-build-fix"],
|
|
16
|
+
"migrate": []
|
|
17
|
+
},
|
|
18
|
+
"agentProfile": {
|
|
19
|
+
"displayName": "C / C++",
|
|
20
|
+
"auditFocus": [
|
|
21
|
+
"raw `new`/`delete` or manual `malloc`/`free` where RAII/a smart pointer (`unique_ptr`/`shared_ptr`) already fits the ownership model",
|
|
22
|
+
"a pointer or reference returned to, or captured from, a local/stack-destroyed object (use-after-free / dangling reference)",
|
|
23
|
+
"a container iterator or pointer used after the container it points into was resized, erased from, or reallocated",
|
|
24
|
+
"an unchecked `malloc`/`new` return, or a missing `free`/destructor on an error/early-return path in C",
|
|
25
|
+
"signed integer overflow, unvalidated array/index bounds, or an out-of-bounds access on a fixed-size buffer",
|
|
26
|
+
"a shared variable read/written from more than one thread with no mutex/atomic guarding it",
|
|
27
|
+
"a memory-safety or UB bug report whose verification step never mentions AddressSanitizer/UndefinedBehaviorSanitizer/ThreadSanitizer"
|
|
28
|
+
],
|
|
29
|
+
"buildCommands": [
|
|
30
|
+
"cmake --preset <configured-preset> (or cmake -S . -B build)",
|
|
31
|
+
"cmake --build build",
|
|
32
|
+
"ctest --test-dir build --output-on-failure",
|
|
33
|
+
"cmake -S . -B build-asan -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer -g' && cmake --build build-asan && ctest --test-dir build-asan"
|
|
34
|
+
],
|
|
35
|
+
"fixGuardrails": [
|
|
36
|
+
"never silence a sanitizer finding by disabling the sanitizer, adding a suppression, or narrowing what it scans instead of fixing the underlying memory/UB bug",
|
|
37
|
+
"never replace a real bounds/null check with a cast or a suppression comment to make a warning disappear",
|
|
38
|
+
"never widen an unsafe C API (`strcpy`, `sprintf`, `gets`) back in while resolving an unrelated failure",
|
|
39
|
+
"fix the smallest root cause; do not refactor unrelated ownership or threading code while resolving a single build/test/sanitizer failure"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.c", "**/*.h", "**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.ipp", "**/*.inl", "**/*.tpp", "**/*.cppm", "**/*.ixx"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# C / C++ coding style
|
|
9
|
+
|
|
10
|
+
Narrows `core-common-rules`' stack-agnostic style rules to modern C++
|
|
11
|
+
(17/20/23) and modern C idiom. Applies to both `.c`/`.h` and
|
|
12
|
+
`.cpp`/`.cc`/`.cxx`/`.hpp`/`.hxx` files — everything not C/C++-specific
|
|
13
|
+
still comes from the common rules this file `extends`.
|
|
14
|
+
|
|
15
|
+
## Naming and structure
|
|
16
|
+
|
|
17
|
+
- `snake_case` for functions, variables, and namespaces; `PascalCase` for
|
|
18
|
+
types (classes, structs, enums); `SCREAMING_SNAKE_CASE` for macros and
|
|
19
|
+
compile-time constants — match whichever convention the project already
|
|
20
|
+
uses over this default when one is established.
|
|
21
|
+
- Keep a header self-contained: `#pragma once` (or a project-consistent
|
|
22
|
+
include guard) at the top, and every symbol the header uses actually
|
|
23
|
+
`#include`d in it — do not rely on an include some other header happens
|
|
24
|
+
to pull in transitively.
|
|
25
|
+
- One translation unit's private helpers go in an anonymous namespace
|
|
26
|
+
(C++) or are declared `static` (C) — do not give internal linkage away
|
|
27
|
+
by leaving a file-local helper externally visible.
|
|
28
|
+
- An identifier's name shrinks with its scope: a loop index is `i`, a
|
|
29
|
+
header-exposed public function gets a full descriptive name.
|
|
30
|
+
|
|
31
|
+
## Modern C++ idiom (17/20/23)
|
|
32
|
+
|
|
33
|
+
- Own a heap allocation with `std::unique_ptr` by default; reach for
|
|
34
|
+
`std::shared_ptr` only when the ownership is genuinely shared (more than
|
|
35
|
+
one owner can outlive any single other owner) — a `shared_ptr` used for
|
|
36
|
+
single ownership just adds atomic refcount overhead and obscures who
|
|
37
|
+
actually owns the object.
|
|
38
|
+
- Prefer `std::make_unique`/`std::make_shared` over a bare `new` passed to
|
|
39
|
+
a smart pointer constructor — it avoids a leak on an exception thrown
|
|
40
|
+
between the `new` and the constructor call, and avoids a second
|
|
41
|
+
allocation for `shared_ptr`'s control block.
|
|
42
|
+
- Use `std::span` (C++20) for a non-owning view over a contiguous range
|
|
43
|
+
instead of a raw `(pointer, length)` pair — it carries bounds
|
|
44
|
+
information the raw pair does not, without owning or copying the data.
|
|
45
|
+
- Move, don't copy, when a value's ownership is transferring: accept
|
|
46
|
+
by-value and `std::move` into place, or take an rvalue-reference
|
|
47
|
+
overload, rather than accepting `const&` and copying when the caller's
|
|
48
|
+
object was about to be discarded anyway.
|
|
49
|
+
- Prefer a range-`for` or a `<ranges>` (C++20) pipeline over a manual
|
|
50
|
+
index loop or raw iterator pair when the loop is a plain traversal —
|
|
51
|
+
keep the manual iterator form for cases that need the iterator itself
|
|
52
|
+
(insertion position, mutation while iterating with an explicit
|
|
53
|
+
erase-returns-next-iterator pattern).
|
|
54
|
+
- `constexpr`/`consteval` any function or value that can be computed at
|
|
55
|
+
compile time; `const` (or nothing, per project convention) otherwise —
|
|
56
|
+
do not default to `#define` for a typed constant.
|
|
57
|
+
|
|
58
|
+
## Modern C safety practices
|
|
59
|
+
|
|
60
|
+
- Prefer a fixed-size or length-tracked buffer API (`snprintf`, not
|
|
61
|
+
`sprintf`; `strncpy`/explicit length checks, not `strcpy`/`strcat`;
|
|
62
|
+
`fgets`, never `gets`) — every string/buffer write states or checks its
|
|
63
|
+
destination's capacity.
|
|
64
|
+
- Check every `malloc`/`calloc`/`realloc` return for `NULL` before
|
|
65
|
+
dereferencing it; on `realloc` failure, keep the original pointer (do
|
|
66
|
+
not overwrite it with the `NULL` return and leak the original block).
|
|
67
|
+
- Prefer C99 designated initializers and stack-based fixed arrays with an
|
|
68
|
+
explicit, checked bound over a raw pointer with an implicit length
|
|
69
|
+
carried only in a comment.
|
|
70
|
+
- Const-qualify a pointer parameter the function does not mutate through
|
|
71
|
+
(`const char *`, not `char *`) — it documents the contract and lets the
|
|
72
|
+
compiler catch an accidental write.
|
|
73
|
+
|
|
74
|
+
## Formatting
|
|
75
|
+
|
|
76
|
+
- Format with the project's configured `clang-format`/`.clang-format` if
|
|
77
|
+
present; do not hand-format around a formatter that is already
|
|
78
|
+
configured.
|
|
79
|
+
- Braces and brace-placement follow the project's existing style over any
|
|
80
|
+
default preference in this file — match neighboring code.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.c", "**/*.h", "**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.ipp", "**/*.inl", "**/*.tpp", "**/*.cppm", "**/*.ixx"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# C / C++ patterns
|
|
9
|
+
|
|
10
|
+
Narrows `core-common-rules`' stack-agnostic design guidance to idiomatic
|
|
11
|
+
C/C++ ownership, resource management, and their common anti-patterns.
|
|
12
|
+
Applies to both C and C++ files.
|
|
13
|
+
|
|
14
|
+
## RAII and ownership (C++)
|
|
15
|
+
|
|
16
|
+
- Every resource (heap memory, a file handle, a mutex lock, a socket) is
|
|
17
|
+
owned by exactly one object whose destructor releases it — a raw `new`
|
|
18
|
+
with a matching `delete` written by hand somewhere else in the function
|
|
19
|
+
is a bug waiting for an early return or an exception to skip the
|
|
20
|
+
`delete`. Wrap it in a smart pointer, `std::lock_guard`/`std::unique_lock`,
|
|
21
|
+
or another RAII type instead.
|
|
22
|
+
- A class that manually manages a resource follows the Rule of
|
|
23
|
+
Zero (own nothing directly — compose from types that already manage
|
|
24
|
+
their own resources) or, when it must own a resource directly, the Rule
|
|
25
|
+
of Five (copy ctor, copy assign, move ctor, move assign, destructor —
|
|
26
|
+
define all five, or `= delete` the ones that should not exist).
|
|
27
|
+
- A function that hands back a resource does so by return value (RVO/move
|
|
28
|
+
makes this free), not by an out-parameter raw pointer the caller must
|
|
29
|
+
remember to release.
|
|
30
|
+
- Never return a pointer or reference to a local (stack) variable, or to a
|
|
31
|
+
member of a temporary — the referent is destroyed when the function
|
|
32
|
+
returns, and the caller is left with a dangling reference.
|
|
33
|
+
|
|
34
|
+
## Manual resource management (C)
|
|
35
|
+
|
|
36
|
+
- Pair every `malloc`/`fopen`/`pthread_mutex_lock`-style acquire with a
|
|
37
|
+
`free`/`fclose`/`pthread_mutex_unlock` on every exit path from the
|
|
38
|
+
function that acquired it, error paths included — a `goto cleanup;`
|
|
39
|
+
pattern with a single exit-and-release block is the idiomatic way to
|
|
40
|
+
keep this from being repeated (and missed) at every early `return`.
|
|
41
|
+
- A function that allocates and returns a pointer documents, in its
|
|
42
|
+
header comment, who owns the result and how it must be released — an
|
|
43
|
+
API with no stated ownership contract is a leak or a double-free
|
|
44
|
+
waiting to happen at the call site.
|
|
45
|
+
- Never call `free` on a pointer twice, and set a pointer to `NULL`
|
|
46
|
+
immediately after freeing it when it could otherwise be read or freed
|
|
47
|
+
again on a later path — a double-free and a use-after-free are both
|
|
48
|
+
more likely once a stale non-null value survives past its `free`.
|
|
49
|
+
|
|
50
|
+
## Containers and iterators
|
|
51
|
+
|
|
52
|
+
- An iterator, pointer, or reference into a `std::vector` (or similar
|
|
53
|
+
contiguous container) is invalidated by any operation that can
|
|
54
|
+
reallocate or shift the underlying storage (`push_back` past capacity,
|
|
55
|
+
`insert`, `erase`) — do not hold one across such a call; re-fetch it, or
|
|
56
|
+
use the iterator `erase`/`insert` return to keep iterating correctly.
|
|
57
|
+
- Prefer `std::vector`/`std::array`/`std::string` over a raw
|
|
58
|
+
owning array or a hand-rolled dynamic buffer — the standard container
|
|
59
|
+
already manages growth, bounds, and destruction correctly.
|
|
60
|
+
|
|
61
|
+
## Composition and interfaces
|
|
62
|
+
|
|
63
|
+
- Prefer composition over deep inheritance hierarchies; reach for
|
|
64
|
+
polymorphism only when callers genuinely need to treat different
|
|
65
|
+
concrete types uniformly through a shared interface, not as a default
|
|
66
|
+
way to share code.
|
|
67
|
+
- A C++ base class meant to be used polymorphically through a base
|
|
68
|
+
pointer/reference has a `virtual` (or `= default`d, explicitly
|
|
69
|
+
`virtual`) destructor — a non-virtual destructor on a polymorphic base
|
|
70
|
+
is undefined behavior when deleting through the base pointer.
|
|
71
|
+
- Keep a public C API's surface small and its ownership rules explicit in
|
|
72
|
+
the header; do not expose an internal struct's full layout when an
|
|
73
|
+
opaque handle plus accessor functions would let the implementation
|
|
74
|
+
change without breaking callers.
|
|
75
|
+
|
|
76
|
+
## Anti-patterns to flag
|
|
77
|
+
|
|
78
|
+
- A "god header" with unrelated free functions and macros accumulating
|
|
79
|
+
under a generic name (`utils.h`, `common.h`) — split by what the code
|
|
80
|
+
actually does.
|
|
81
|
+
- C-style casts (`(Type)x`) in C++ where a named cast
|
|
82
|
+
(`static_cast`/`dynamic_cast`/`const_cast`/`reinterpret_cast`) would say
|
|
83
|
+
which conversion is actually intended and let the compiler catch the
|
|
84
|
+
wrong one.
|
|
85
|
+
- Output parameters used to fake multiple return values where
|
|
86
|
+
`std::tuple`/`std::pair`/a small struct (or, in C, a returned struct)
|
|
87
|
+
would be clearer and harder to call incorrectly.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.c", "**/*.h", "**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.ipp", "**/*.inl", "**/*.tpp", "**/*.cppm", "**/*.ixx"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# C / C++ security
|
|
9
|
+
|
|
10
|
+
Narrows `core-common-rules`' stack-agnostic security rules to C/C++
|
|
11
|
+
memory-safety and undefined-behavior risks, and the sanitizers/safe APIs
|
|
12
|
+
to use instead. Applies to both C and C++ files.
|
|
13
|
+
|
|
14
|
+
## Memory safety
|
|
15
|
+
|
|
16
|
+
- Never dereference, write through, or `free` a pointer after it has been
|
|
17
|
+
freed or has gone out of scope (use-after-free) — set a freed pointer
|
|
18
|
+
to `NULL` when it could be read again, and never return a pointer or
|
|
19
|
+
reference to a stack-local object.
|
|
20
|
+
- Never `free`/`delete` the same pointer twice (double-free) — a
|
|
21
|
+
freed-and-nulled pointer makes a second `free`/`delete` a harmless
|
|
22
|
+
no-op instead of heap corruption.
|
|
23
|
+
- Bounds-check every array/buffer index and every pointer arithmetic
|
|
24
|
+
expression built from untrusted input before it is used to read or
|
|
25
|
+
write memory — an off-by-one or an unchecked length from user input is
|
|
26
|
+
the most common route to a buffer overflow.
|
|
27
|
+
- Never use `strcpy`, `strcat`, `sprintf`, or `gets` on data whose length
|
|
28
|
+
is not already statically known to fit the destination — use
|
|
29
|
+
`snprintf`, a length-checked copy, or a `std::string`/`std::vector`
|
|
30
|
+
that manages its own capacity.
|
|
31
|
+
|
|
32
|
+
## Undefined behavior
|
|
33
|
+
|
|
34
|
+
- Signed integer overflow is undefined behavior in C/C++ (unlike
|
|
35
|
+
unsigned wraparound, which is well-defined modular arithmetic) — check
|
|
36
|
+
bounds before an addition/multiplication that could overflow a signed
|
|
37
|
+
type, or use an unsigned/wider type, or a checked-arithmetic helper,
|
|
38
|
+
for a bounds or size computation derived from untrusted input.
|
|
39
|
+
- Never read an uninitialized local variable or heap allocation — value-
|
|
40
|
+
initialize (`Type x{};` in C++, an explicit `= 0`/`memset` in C) any
|
|
41
|
+
variable whose first use might be a read, and never rely on whatever
|
|
42
|
+
value happened to be left on the stack or heap.
|
|
43
|
+
- Never access an object through a pointer of an incompatible type (a
|
|
44
|
+
strict-aliasing violation) — use `memcpy` for a type-punning copy, or
|
|
45
|
+
(C++20+) `std::bit_cast`, instead of reinterpreting a pointer's type
|
|
46
|
+
and dereferencing it.
|
|
47
|
+
- Do not read past the end of an array via pointer arithmetic even when
|
|
48
|
+
the read "usually" lands in valid memory — an out-of-bounds read past
|
|
49
|
+
the last element is undefined behavior regardless of what happens to be
|
|
50
|
+
mapped there.
|
|
51
|
+
|
|
52
|
+
## Concurrency
|
|
53
|
+
|
|
54
|
+
- Every variable read from more than one thread while at least one thread
|
|
55
|
+
writes it is guarded by a mutex, or is a `std::atomic`/C11 `_Atomic`
|
|
56
|
+
type — an unsynchronized shared read/write is a data race, which is
|
|
57
|
+
undefined behavior in both C and C++, not merely "occasionally wrong."
|
|
58
|
+
- Acquire locks in a consistent global order across the codebase to avoid
|
|
59
|
+
deadlock; prefer `std::lock_guard`/`std::scoped_lock` (which also
|
|
60
|
+
supports locking multiple mutexes deadlock-free in one call) over
|
|
61
|
+
manual `lock()`/`unlock()` pairs, since a manual pair skips the unlock
|
|
62
|
+
on an early return or exception.
|
|
63
|
+
|
|
64
|
+
## Sanitizers
|
|
65
|
+
|
|
66
|
+
- Build and run with AddressSanitizer + UndefinedBehaviorSanitizer
|
|
67
|
+
(`-fsanitize=address,undefined -fno-omit-frame-pointer -g`, Clang/GCC)
|
|
68
|
+
whenever investigating a memory-safety or UB bug report, or before
|
|
69
|
+
trusting a fix for one — ASan catches use-after-free, double-free,
|
|
70
|
+
buffer overflow, and heap corruption at the exact faulting access; UBSan
|
|
71
|
+
catches signed overflow, misaligned access, and other UB the compiler
|
|
72
|
+
is otherwise free to miscompile around silently.
|
|
73
|
+
- Build and run with ThreadSanitizer (`-fsanitize=thread`) — built as a
|
|
74
|
+
separate binary from ASan/UBSan, the two runtimes are not composable in
|
|
75
|
+
one build — whenever investigating a suspected data race or before
|
|
76
|
+
trusting a fix for one.
|
|
77
|
+
- A memory-safety, UB, or concurrency bug report is not actually verified
|
|
78
|
+
fixed until it has been reproduced and re-run under the relevant
|
|
79
|
+
sanitizer and comes back clean; a fix that only "looks right" by
|
|
80
|
+
inspection, or that only passes without a sanitizer attached, has not
|
|
81
|
+
been verified.
|
|
82
|
+
|
|
83
|
+
## Dependency and input hygiene
|
|
84
|
+
|
|
85
|
+
- Validate the length/format of any externally-sourced buffer (network,
|
|
86
|
+
file, IPC, command-line argument) before parsing it — do not assume a
|
|
87
|
+
length field in the input itself is trustworthy without a sanity check
|
|
88
|
+
against the buffer's actual size.
|
|
89
|
+
- Never hard-code a credential, API key, or signing secret in source;
|
|
90
|
+
load it from environment/secret storage the project already uses.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
extends: common
|
|
3
|
+
paths: ["**/*.c", "**/*.h", "**/*.cpp", "**/*.cc", "**/*.cxx", "**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.ipp", "**/*.inl", "**/*.tpp", "**/*.cppm", "**/*.ixx"]
|
|
4
|
+
metadata:
|
|
5
|
+
origin: authored
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# C / C++ testing
|
|
9
|
+
|
|
10
|
+
Narrows `core-common-rules`' stack-agnostic testing rules to GoogleTest
|
|
11
|
+
(and Catch2, where a project already uses it) plus sanitizer-backed
|
|
12
|
+
verification. Applies to both C and C++ test files.
|
|
13
|
+
|
|
14
|
+
## Layout and naming
|
|
15
|
+
|
|
16
|
+
- Tests live under a project's own existing test tree (commonly
|
|
17
|
+
`tests/`, `test/`, or beside the source with a `_test.cpp` suffix) —
|
|
18
|
+
match whichever convention the project already uses, do not invent a
|
|
19
|
+
second one.
|
|
20
|
+
- `TEST(SuiteName, CaseName)` for a standalone case; `TEST_F(FixtureName,
|
|
21
|
+
CaseName)` when the case needs shared `SetUp()`/`TearDown()` state via a
|
|
22
|
+
fixture class derived from `::testing::Test`. Name suites/cases for the
|
|
23
|
+
behavior under test, not `Test1`/`Test2`.
|
|
24
|
+
- `TEST_P` with `INSTANTIATE_TEST_SUITE_P` for a parameterized case run
|
|
25
|
+
against a range of inputs, instead of copy-pasting the same body with
|
|
26
|
+
different literals across several `TEST`s.
|
|
27
|
+
|
|
28
|
+
## Fixtures and lifecycle
|
|
29
|
+
|
|
30
|
+
- Put shared per-test setup in `SetUp()` and teardown in `TearDown()`,
|
|
31
|
+
not the fixture's constructor/destructor, when the setup can fail in a
|
|
32
|
+
way the test should assert on (`SetUp()` runs inside the test's own
|
|
33
|
+
assertion context; a constructor failure does not).
|
|
34
|
+
- A fixture that owns a resource (a temp file, a mock server, a thread)
|
|
35
|
+
releases it in `TearDown()` even when the test body fails an assertion
|
|
36
|
+
partway through — do not rely on a `TEST_F` body reaching a cleanup
|
|
37
|
+
statement at its end.
|
|
38
|
+
|
|
39
|
+
## Assertions
|
|
40
|
+
|
|
41
|
+
- `ASSERT_*` when the test cannot meaningfully continue after a failure
|
|
42
|
+
(a null pointer that every following line would dereference);
|
|
43
|
+
`EXPECT_*` when later checks in the same test still give useful
|
|
44
|
+
information after one fails.
|
|
45
|
+
- `ASSERT_DEATH`/`EXPECT_DEATH` for code whose contract is to terminate
|
|
46
|
+
the process on a given input (an assertion failure, a `CHECK`) — set
|
|
47
|
+
`GTEST_FLAG_SET(death_test_style, "threadsafe")` (or the project's own
|
|
48
|
+
configured style) when the test binary is multi-threaded, since the
|
|
49
|
+
default `fast` style can misbehave by forking a process with more than
|
|
50
|
+
one live thread.
|
|
51
|
+
- Compare floating-point values with `EXPECT_NEAR`/`EXPECT_FLOAT_EQ`, not
|
|
52
|
+
`EXPECT_EQ`, unless the value is provably exact (an integer-valued
|
|
53
|
+
computation stored in a float).
|
|
54
|
+
|
|
55
|
+
## Determinism and concurrency
|
|
56
|
+
|
|
57
|
+
- Never synchronize a multi-threaded test with `sleep`/`usleep`/
|
|
58
|
+
`std::this_thread::sleep_for` to "give the other thread time" — join
|
|
59
|
+
the thread, wait on a condition variable, or use a future/promise so
|
|
60
|
+
the test is deterministic and not flaky under load.
|
|
61
|
+
- A test exercising concurrent code is run under ThreadSanitizer
|
|
62
|
+
(`-fsanitize=thread`) at least once before being trusted, since a data
|
|
63
|
+
race can pass every assertion while still being undefined behavior.
|
|
64
|
+
|
|
65
|
+
## Sanitizer-backed verification
|
|
66
|
+
|
|
67
|
+
- Build and run the test suite under AddressSanitizer + UndefinedBehavior-
|
|
68
|
+
Sanitizer for any change touching manual memory management, buffers, or
|
|
69
|
+
pointer arithmetic — a leaking or corrupting test can still report
|
|
70
|
+
green under plain assertions while ASan/UBSan catch the actual bug.
|
|
71
|
+
- New behavior gets a new test in the same change; a bug fix (especially
|
|
72
|
+
a memory-safety or UB fix) gets a regression test that reproduces the
|
|
73
|
+
failure under the relevant sanitizer before the fix and passes clean
|
|
74
|
+
after it.
|
|
75
|
+
|
|
76
|
+
## Fuzzing
|
|
77
|
+
|
|
78
|
+
- A parser, decoder, or anything else that touches untrusted input
|
|
79
|
+
formats gets a libFuzzer harness (`LLVMFuzzerTestOneInput`, built with
|
|
80
|
+
`-fsanitize=fuzzer,address,undefined`) when the project already has
|
|
81
|
+
fuzzing infrastructure, or a documented seed corpus ready for one to be
|
|
82
|
+
added — do not skip fuzz coverage for a new untrusted-input parser
|
|
83
|
+
solely because unit tests pass.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: c-cpp-build-fix
|
|
3
|
+
description: "Use when a C/C++ build fails -- CMake configure/link errors, missing headers, template/generic-instantiation errors, ABI/linker mismatches -- or when a sanitizer (ASan/UBSan/TSan) or ctest run reports a memory-safety, UB, or race failure, with the smallest root-cause fix."
|
|
4
|
+
triggers:
|
|
5
|
+
- "cmake build is failing"
|
|
6
|
+
- "fix this linker error"
|
|
7
|
+
- "AddressSanitizer reports a heap-buffer-overflow"
|
|
8
|
+
- "UBSan reports signed overflow"
|
|
9
|
+
- "ThreadSanitizer flags an unsynchronized access to this shared counter"
|
|
10
|
+
- "fix this C++ template instantiation error"
|
|
11
|
+
metadata:
|
|
12
|
+
origin: authored
|
|
13
|
+
category: build-fix
|
|
14
|
+
version: "1.0.0"
|
|
15
|
+
compatible_harnesses: "claude,codex,cursor,zed,opencode"
|
|
16
|
+
license: "MIT"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# C / C++ build fix
|
|
20
|
+
|
|
21
|
+
Resolve a C/C++ build/configure/link failure, or a sanitizer/`ctest`
|
|
22
|
+
failure — with the smallest change that fixes the actual root cause.
|
|
23
|
+
`rules/coding-style.mdc`, `rules/patterns.mdc`, and `rules/security.mdc`
|
|
24
|
+
govern what a "correct" fix looks like; this skill never reaches for a
|
|
25
|
+
suppression instead of a fix.
|
|
26
|
+
|
|
27
|
+
## Workflow
|
|
28
|
+
|
|
29
|
+
### Step 1: Reproduce and classify
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cmake --build build
|
|
33
|
+
ctest --test-dir build --output-on-failure
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Read the exact error text and classify it:
|
|
37
|
+
|
|
38
|
+
- **Configure error** (CMake cache mismatch, a missing `find_package`
|
|
39
|
+
dependency, a stale `build/` directory from a changed toolchain).
|
|
40
|
+
- **Compile error** (undefined symbol, type mismatch, missing header,
|
|
41
|
+
wrong argument count/type).
|
|
42
|
+
- **Template/generic-instantiation error** (a template that cannot
|
|
43
|
+
deduce its argument, a constraint/concept that is not satisfied, an
|
|
44
|
+
incomplete type used where a complete one is required).
|
|
45
|
+
- **Link/ABI error** (undefined reference, a symbol built against a
|
|
46
|
+
different ABI or ODR-violating duplicate definition across translation
|
|
47
|
+
units, a missing `target_link_libraries`).
|
|
48
|
+
- **Sanitizer failure** (ASan `heap-buffer-overflow`/`use-after-free`,
|
|
49
|
+
UBSan `signed integer overflow`/`misaligned address`, TSan
|
|
50
|
+
`data race`) — read the sanitizer's full stack trace; it names the
|
|
51
|
+
exact faulting access and, for TSan, both racing accesses.
|
|
52
|
+
- **Test failure** (`ctest` reports a failing case with no sanitizer
|
|
53
|
+
involved).
|
|
54
|
+
|
|
55
|
+
### Step 2: Fix by category
|
|
56
|
+
|
|
57
|
+
**Configure/CMake:** re-run configure after a clean `build/` directory
|
|
58
|
+
when the cache is simply stale against changed `CMakeLists.txt`. For a
|
|
59
|
+
genuine missing dependency, add the correct `find_package`/
|
|
60
|
+
`target_link_libraries` rather than hand-pointing at a local path that
|
|
61
|
+
happens to work on one machine.
|
|
62
|
+
|
|
63
|
+
**Compile error:** fix the actual type/signature/missing-include issue
|
|
64
|
+
the compiler names. A missing header is fixed by including the header
|
|
65
|
+
that actually declares the symbol, not by forward-declaring around it
|
|
66
|
+
unless the project's own convention already does that intentionally.
|
|
67
|
+
|
|
68
|
+
**Template/generic-instantiation:** check whether the call site can
|
|
69
|
+
supply the type argument explicitly, or whether a concept/`static_assert`
|
|
70
|
+
is correctly rejecting an unsupported type (in which case the fix is at
|
|
71
|
+
the call site, not the template). Do not loosen a constraint just to make
|
|
72
|
+
an unrelated instantiation compile.
|
|
73
|
+
|
|
74
|
+
**Link/ABI:** an undefined reference usually means a missing
|
|
75
|
+
`target_link_libraries` entry or a translation unit not compiled into
|
|
76
|
+
the target — fix the CMake target graph. An ODR violation (the same
|
|
77
|
+
symbol defined differently in two translation units) is fixed by
|
|
78
|
+
unifying the definition, never by reordering link order to hide it.
|
|
79
|
+
|
|
80
|
+
**Sanitizer failure (ASan/UBSan):** read the exact faulting access from
|
|
81
|
+
the report (allocation site, free site if UAF, faulting instruction) and
|
|
82
|
+
add the actual missing check/ownership fix at that point — a bounds
|
|
83
|
+
check, a lifetime fix (return by value instead of a reference into a
|
|
84
|
+
temporary), or a `NULL` check. Never rebuild without the sanitizer to
|
|
85
|
+
make the failure "go away"; that hides the bug, it does not fix it.
|
|
86
|
+
|
|
87
|
+
**Sanitizer failure (TSan):** add synchronization (mutex, atomic, or a
|
|
88
|
+
join/wait) at the specific shared-state access TSan names for both
|
|
89
|
+
racing accesses — do not just serialize the whole test or add a `sleep`
|
|
90
|
+
to hide the timing window.
|
|
91
|
+
|
|
92
|
+
**Test failure (no sanitizer):** fix the assertion or the code it tests,
|
|
93
|
+
whichever is actually wrong per the spec — never loosen an assertion just
|
|
94
|
+
to make it pass.
|
|
95
|
+
|
|
96
|
+
### Step 3: Verify
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cmake --build build
|
|
100
|
+
ctest --test-dir build --output-on-failure
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Re-run under the sanitizer that originally caught the failure (ASan+UBSan
|
|
104
|
+
or TSan) if the failure came from one — the fix is not verified until the
|
|
105
|
+
sanitizer run is also clean, not just the plain build.
|
|
106
|
+
|
|
107
|
+
### Step 4: Report
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
Fixed: src/parser/token_stream.cpp — heap-buffer-overflow in
|
|
111
|
+
TokenStream::peek() (ASan)
|
|
112
|
+
- Root cause: peek() read one byte past the buffer on the last token
|
|
113
|
+
boundary; missing bounds check before the trailing-byte read
|
|
114
|
+
- Build + ctest + ASan/UBSan re-run all pass
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
State the root cause in one sentence, not just "fixed the error."
|
|
118
|
+
|
|
119
|
+
## Rules
|
|
120
|
+
|
|
121
|
+
- Find and fix the smallest change that addresses the actual root cause —
|
|
122
|
+
never widen a fix beyond what the failure requires.
|
|
123
|
+
- NEVER disable, narrow the scope of, or suppress a sanitizer finding
|
|
124
|
+
instead of fixing the underlying memory-safety/UB/race bug it caught.
|
|
125
|
+
- NEVER reintroduce an unsafe API (`strcpy`, `sprintf`, `gets`) while
|
|
126
|
+
resolving an unrelated failure.
|
|
127
|
+
- NEVER loosen a template constraint, a `static_assert`, or a test
|
|
128
|
+
assertion just to make a failure disappear without understanding why
|
|
129
|
+
it was there.
|
|
130
|
+
- NEVER change the project's declared C/C++ standard just to make an
|
|
131
|
+
error disappear without understanding why the code needs it.
|
|
132
|
+
|
|
133
|
+
## Red Flags
|
|
134
|
+
|
|
135
|
+
| Rationalization | Why it is wrong |
|
|
136
|
+
|---|---|
|
|
137
|
+
| "I'll just turn off ASan for this test so CI goes green" | Silences the finding without fixing the memory-safety bug it caught; fix the bounds/lifetime issue at the site ASan names instead |
|
|
138
|
+
| "This data race is rare, I'll add a sleep to make it stop happening" | A `sleep` narrows the timing window without removing the race; TSan will still be correct that the access is unsynchronized, and the race can still fire under different scheduling |
|
|
139
|
+
| "I'll widen this concept/constraint so more types compile" | Loosening a constraint to silence one instantiation failure can let a genuinely unsupported type instantiate the template incorrectly elsewhere |
|
|
140
|
+
| "sprintf is fine here, the buffer is 'probably' big enough" | "Probably" is exactly the assumption that turns into a buffer overflow the first time an input is bigger than expected; use `snprintf` |
|
|
141
|
+
|
|
142
|
+
## Verification
|
|
143
|
+
|
|
144
|
+
Do not report the fix done until all of the following hold:
|
|
145
|
+
|
|
146
|
+
- `cmake --build build` and `ctest --test-dir build --output-on-failure`
|
|
147
|
+
both exit 0.
|
|
148
|
+
- If the original failure came from a sanitizer, the sanitizer build/run
|
|
149
|
+
is re-verified clean, not just the plain build.
|
|
150
|
+
- The change is the smallest one that addresses the stated root cause —
|
|
151
|
+
no unrelated files touched.
|
|
152
|
+
- The report states the root cause in one sentence, not just "build now
|
|
153
|
+
passes."
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"triggers": {
|
|
3
|
+
"positive": [
|
|
4
|
+
"Our ctest suite segfaults inside the ASan build after the latest merge, can you track down what's reading past the buffer",
|
|
5
|
+
"ThreadSanitizer flagged a data race, help me fix it",
|
|
6
|
+
"UndefinedBehaviorSanitizer is reporting signed integer overflow here",
|
|
7
|
+
"Our CMake configure step is failing with an undefined reference",
|
|
8
|
+
"This template won't instantiate, fix the compile error",
|
|
9
|
+
"The build is failing with a missing header error",
|
|
10
|
+
"Our test binary crashes under ASan, track down the root cause"
|
|
11
|
+
],
|
|
12
|
+
"negative": [
|
|
13
|
+
"Implement this new feature from scratch, no build errors involved",
|
|
14
|
+
"Write GoogleTest cases for this already-working class",
|
|
15
|
+
"Review this diff for memory safety before merging",
|
|
16
|
+
"Fix this failing go build for the worker package",
|
|
17
|
+
"Our npm build is failing with a webpack error",
|
|
18
|
+
"Fix this failing pytest collection error"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"scenarios": [
|
|
22
|
+
{
|
|
23
|
+
"id": "asan-heap-overflow-fix-not-suppress",
|
|
24
|
+
"prompt": "AddressSanitizer reports:\n\n```\nERROR: AddressSanitizer: heap-buffer-overflow on address ...\nREAD of size 1 at ... thread T0\n #0 TokenStream::peek() token_stream.cpp:42\n```\n\nHow do I fix this?",
|
|
25
|
+
"strictness": "high",
|
|
26
|
+
"expected_behavior": [
|
|
27
|
+
{
|
|
28
|
+
"grader": "judge",
|
|
29
|
+
"rubric": "A correct answer treats the ASan report as pointing at a real out-of-bounds read in TokenStream::peek() at line 42, and fixes it by adding or correcting the bounds check that prevents reading past the buffer -- it does not suggest disabling, narrowing, or otherwise routing around the sanitizer to make the finding go away.",
|
|
30
|
+
"pass_criteria": [
|
|
31
|
+
"Identifies the finding as a genuine out-of-bounds read at the reported location (TokenStream::peek(), token_stream.cpp:42), not a false positive to be dismissed.",
|
|
32
|
+
"Proposes a concrete fix: adding/correcting a bounds check (e.g. comparing the read position against the buffer's length) before the read that peek() performs.",
|
|
33
|
+
"States that the fix should be re-verified by re-running the test under the ASan build, not just a plain build."
|
|
34
|
+
],
|
|
35
|
+
"fail_criteria": [
|
|
36
|
+
"Recommends disabling ASan, removing the sanitizer flag, adding an ASan suppress list/suppression entry, or excluding token_stream.cpp/this test from the sanitized build instead of fixing the out-of-bounds read. Mentioning that only to warn against it is not a failure."
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"calibration": {
|
|
41
|
+
"known_right": "This is a real out-of-bounds read, not a false positive -- ASan is pointing at token_stream.cpp:42 inside TokenStream::peek(). Read the surrounding code: peek() is almost certainly reading `buffer_[pos_]` (or similar) without first checking that `pos_` is still within `buffer_`'s bounds, most likely on the last token where `pos_` has already reached the end. Fix it by adding the missing bounds check before the read, e.g. `if (pos_ >= buffer_.size()) return kEof;` (or whatever peek()'s documented behavior should be at end-of-input) before the indexing. Once the check is in, rebuild the ASan configuration (`cmake --build build-asan`) and re-run the specific test that exercises this path (`ctest --test-dir build-asan -R TokenStreamTest --output-on-failure`) to confirm ASan no longer reports the overflow -- a plain, non-sanitized rebuild passing is not sufficient confirmation.",
|
|
42
|
+
"known_wrong": "ASan is pretty aggressive and throws false positives on tight parsing loops like this fairly often. Simplest fix is to just suppress it -- add token_stream.cpp's TokenStream::peek to an ASan suppress list (or drop -fsanitize=address for that one translation unit) so the rest of the sanitized build stays clean and this known-noisy function doesn't keep flagging.",
|
|
43
|
+
"vague": "Take a look at what's happening around line 42 in peek() and make sure the bounds are being handled correctly there.",
|
|
44
|
+
"subtle_wrong": "The read at line 42 is one byte past the buffer on the very last token, so the safest fix is to just allocate the underlying buffer with one extra byte of padding (`buffer_.resize(buffer_.size() + 1)`) when TokenStream is constructed, so that stray read always lands in valid memory. That way peek()'s existing logic doesn't need to change at all, and the ASan report goes away because the read is no longer actually out of bounds."
|
|
45
|
+
},
|
|
46
|
+
"anti_patterns": ["suppress"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "tsan-race-add-sync-not-serialize",
|
|
50
|
+
"prompt": "ThreadSanitizer reports a data race:\n\n```\nWARNING: ThreadSanitizer: data race\n Write of size 8 at 0x... by thread T1:\n #0 Cache::put(...) cache.cpp:31\n Previous read of size 8 at 0x... by thread T2:\n #0 Cache::get(...) cache.cpp:18\n```\n\nHow should I fix this?",
|
|
51
|
+
"strictness": "high",
|
|
52
|
+
"expected_behavior": [
|
|
53
|
+
{
|
|
54
|
+
"grader": "judge",
|
|
55
|
+
"rubric": "A correct answer adds real synchronization (a mutex guarding both Cache::get and Cache::put's access to the shared state, or converting the field to std::atomic if a single word suffices) at the specific accesses TSan names, rather than working around the race by serializing the whole test suite or disabling the sanitizer.",
|
|
56
|
+
"pass_criteria": [
|
|
57
|
+
"Names a concrete synchronization mechanism -- a mutex (e.g. std::lock_guard/std::mutex) guarding both cache.cpp:31 (put) and cache.cpp:18 (get), or std::atomic if the shared data is a single word -- applied at the actual accesses TSan flagged.",
|
|
58
|
+
"States that both the read in get() and the write in put() need to be covered by the same synchronization, not just one side.",
|
|
59
|
+
"Does not propose serializing the test (running it single-threaded, adding a global test-only lock around the whole test body) or disabling/skipping TSan as the fix."
|
|
60
|
+
],
|
|
61
|
+
"fail_criteria": [
|
|
62
|
+
"Recommends running the test single-threaded, adding a sleep to reduce the race window, or disabling/skipping ThreadSanitizer instead of adding real synchronization to Cache::get/Cache::put. Mentioning that only to warn against it is not a failure."
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"calibration": {
|
|
67
|
+
"known_right": "TSan is naming the actual unsynchronized pair: a write in Cache::put() at cache.cpp:31 racing a read in Cache::get() at cache.cpp:18, both touching the same shared field with no lock between them. Add a std::mutex member to Cache guarding whatever field both functions touch, and take it in both: `std::lock_guard<std::mutex> lock(mutex_);` at the top of both get() and put() before they read/write the shared state. If the shared data really is just a single word (a counter, a pointer swap), std::atomic<T> with appropriate memory order is a lighter-weight alternative to a full mutex -- but either way, both the read side and the write side need the same synchronization, not just one of them. Rebuild under TSan (`-fsanitize=thread`) and re-run the specific test to confirm the warning is gone before considering this fixed.",
|
|
68
|
+
"known_wrong": "The simplest fix is to just run this particular test single-threaded -- add `GTEST_FLAG_SET` or a test-only global lock around the whole test body so get() and put() never actually execute concurrently during the test run. That removes the race TSan is seeing without having to add locking to Cache itself, which keeps the production code's hot path exactly as fast as it is today.",
|
|
69
|
+
"vague": "Cache::get and Cache::put seem to be touching something shared without any protection, that probably needs to be locked down.",
|
|
70
|
+
"subtle_wrong": "Add the mutex, but only take the lock inside put() around the write at cache.cpp:31 -- get() at cache.cpp:18 is just reading a value, and a plain read of an already-word-aligned field is safe to leave unlocked on most platforms since a single aligned load or store doesn't tear. That gets the write side properly guarded, which is the side TSan is really worried about, without slowing down the more frequent read path with lock overhead."
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|