cybersecurity · beginner · ~12 min

Render a finding struct as Markdown

Build a bounded-buffer text formatter with `snprintf` and proper overflow handling.

Challenge

Turn a single security finding into a Markdown writeup with a bounded, overflow-safe formatter.

Task

Implement int render_finding(const finding_t *f, char *out, size_t cap) over:

typedef struct {
    const char *title;
    const char *severity;
    const char *asset;
    const char *evidence;
    const char *recommendation;
} finding_t;

It writes f into out (capacity cap) as Markdown and returns the number of bytes written (excluding the trailing NUL), or -1 on failure.

Input

  • f: a pointer to a fully-populated finding_t the grader passes.
  • out: caller-provided buffer of size cap.

Output

Bytes written (excluding NUL), or -1 if f/out is NULL, cap == 0, any field of f is NULL, or the text would not fit. The exact layout (note the blank lines):

## <title>
**Severity:** <severity>
**Asset:** <asset>

### Evidence
<evidence>

### Recommendation
<recommendation>

(a trailing newline follows the recommendation).

Example

title="Missing HSTS header", severity="Medium", ...
->  buffer begins "## Missing HSTS header\n**Severity:** Medium\n..."
    return value == strlen(out)

tiny 16-byte buffer   ->   -1
NULL finding          ->   -1
finding with a NULL field   ->   -1

Edge cases

  • A buffer too small for the full text returns -1 (no truncated output relied upon).
  • NULL f, NULL out, cap == 0, or any NULL field returns -1.

Rules

  • Use snprintf and check its return: < 0 or >= cap means overflow → return -1.

Why this matters

Findings without a writeup don't ship. A bounded snprintf formatter turns a struct into a deliverable.

Input format

A pointer to a populated finding_t, an output buffer out, and its capacity cap.

Output format

Bytes written excluding the NUL, or -1 on any NULL input, cap==0, NULL field, or overflow.

Constraints

Use snprintf; treat return <0 or >=cap as overflow; reject any NULL.

Starter code

#include <stddef.h>
typedef struct {
    const char *title;
    const char *severity;
    const char *asset;
    const char *evidence;
    const char *recommendation;
} finding_t;

int render_finding(const finding_t *f, char *out, size_t cap) {
    /* TODO */
    (void)f; (void)out; (void)cap;
    return -1;
}

Common mistakes

Trusting snprintf's return without checking against cap. Forgetting the blank lines between sections. NULL inputs not rejected.

Edge cases to handle

Tiny buffer → -1. NULL pointer or NULL field → -1. Exact-fit buffer → bytes match strlen(out).

Complexity

O(n) where n is the sum of input string lengths.

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.