cybersecurity · beginner · ~12 min

Render a finding struct as Markdown

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

Challenge

Your job

Implement:

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);

Render f into out as Markdown. Return the number of bytes written (excluding the trailing NUL), or -1 if the buffer would overflow.

Expected format (note the blank lines)

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

### Evidence
<evidence>

### Recommendation
<recommendation>

Rules

  • Use snprintf. Check its return value: < 0 or >= cap → return -1.
  • NULL f, NULL out, or cap == 0 → return -1.
  • Any NULL field inside f → return -1.

Hints

  1. (concept) One snprintf with the whole template is simplest. The format string spans seven %s substitutions.
  2. (direction) int n = snprintf(out, cap, fmt, title, severity, asset, evidence, recommendation); if (n < 0 || (size_t)n >= cap) return -1; return n;
  3. (common bug) Forgetting that snprintf returns the count it WOULD have written if the buffer were big enough — so n >= cap means overflow.

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 finding_t + an output buffer + its capacity.

Output format

Bytes written (excluding NUL), or -1 on any failure.

Constraints

Use snprintf. Check overflow. Return -1 on 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.