Skip to content

CSP Builder — C source

Build a Content-Security-Policy header interactively. Toggle directives, add sources, see the assembled header in real time — with a security score that flags unsafe sources.

This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.

/*
 * csp-builder — Content-Security-Policy assembly, parsing, linting and scoring.
 *
 * Language: C (C11, standard library only)
 * Source:   CosmoDev polyglot showcase port of the CSP Builder tool, ported
 *           from src/lib/csp-builder.ts (the canonical TypeScript
 *           implementation).
 * License:  display source — part of CosmoDev's polyglot tool pages.
 *
 * A CSP is modeled as a map of directive -> source list. Build assembles the
 * map into the header string (directives in catalog order, then any unknown
 * directives in insertion order); parse reads a header back into the map.
 * Neither function ever fails on content — parse is lenient by design so a
 * pasted real-world header always yields something editable.
 *
 * C has no dictionary, and JavaScript objects iterate in insertion order, so
 * the policy is an ordered vector of entries. Lookup is a linear scan: a CSP
 * has at most a couple of dozen directives, so this is both simpler and
 * faster than hashing.
 *
 * Build: cc -std=c11 csp-builder.c
 */

#include <ctype.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* -------------------------------------------------------------------------
 * Catalog types
 * ------------------------------------------------------------------------- */

/** How a directive takes its value: a source list, a single URL, or a bare flag. */
typedef enum {
    CSP_KIND_SOURCES,
    CSP_KIND_URL,
    CSP_KIND_FLAG
} csp_directive_kind;

/** How much exposure the directive controls (drives UI emphasis). */
typedef enum {
    CSP_RISK_LOW,
    CSP_RISK_MEDIUM,
    CSP_RISK_HIGH
} csp_directive_risk;

/** One entry of the built-in directive catalog. */
typedef struct {
    const char *name;
    csp_directive_kind kind;
    const char *description;
    csp_directive_risk risk;
    /** Sources inserted when the directive is enabled in the UI (NULL-terminated). */
    const char *const *default_sources;
} csp_directive_info;

static const char *const CSP_SRC_SELF[] = { "'self'", NULL };
static const char *const CSP_SRC_NONE[] = { "'none'", NULL };
static const char *const CSP_SRC_EMPTY[] = { NULL };

/** The catalog, in canonical build/display order. */
static const csp_directive_info CSP_DIRECTIVES[] = {
    { "default-src", CSP_KIND_SOURCES,
      "Fallback for every fetch directive you do not set explicitly. Set this "
      "first, then tighten individual directives.",
      CSP_RISK_MEDIUM, CSP_SRC_SELF },
    { "script-src", CSP_KIND_SOURCES,
      "Where scripts may load from. The single most important XSS control - "
      "keep it as tight as you can.",
      CSP_RISK_HIGH, CSP_SRC_SELF },
    { "style-src", CSP_KIND_SOURCES,
      "Where stylesheets may load from. Also gates inline style attributes.",
      CSP_RISK_MEDIUM, CSP_SRC_SELF },
    { "img-src", CSP_KIND_SOURCES,
      "Where images and favicons may load from.",
      CSP_RISK_LOW, CSP_SRC_SELF },
    { "connect-src", CSP_KIND_SOURCES,
      "Which URLs scripts may connect to (fetch, XHR, WebSocket). Your "
      "data-exfiltration boundary.",
      CSP_RISK_MEDIUM, CSP_SRC_SELF },
    { "font-src", CSP_KIND_SOURCES,
      "Where web fonts may load from.",
      CSP_RISK_LOW, CSP_SRC_SELF },
    { "frame-src", CSP_KIND_SOURCES,
      "Which URLs may be embedded as child browsing contexts (iframe, frame).",
      CSP_RISK_LOW, CSP_SRC_SELF },
    { "media-src", CSP_KIND_SOURCES,
      "Where audio and video may load from.",
      CSP_RISK_LOW, CSP_SRC_SELF },
    { "object-src", CSP_KIND_SOURCES,
      "Where plugin content (object, embed, applet) may load from. Almost "
      "always should be 'none'.",
      CSP_RISK_HIGH, CSP_SRC_NONE },
    { "base-uri", CSP_KIND_SOURCES,
      "Which URLs may set the document base. Restrict to 'self' to block "
      "<base> hijacking of relative URLs.",
      CSP_RISK_HIGH, CSP_SRC_SELF },
    { "form-action", CSP_KIND_SOURCES,
      "Where forms may submit to. Does not fall back to default-src.",
      CSP_RISK_MEDIUM, CSP_SRC_SELF },
    { "frame-ancestors", CSP_KIND_SOURCES,
      "Which parents may embed this page (clickjacking control). Ignored "
      "inside a <meta> tag - header delivery only.",
      CSP_RISK_MEDIUM, CSP_SRC_SELF },
    { "report-uri", CSP_KIND_URL,
      "URL where the browser posts violation reports. Pair with a report "
      "collector.",
      CSP_RISK_LOW, CSP_SRC_EMPTY },
    { "upgrade-insecure-requests", CSP_KIND_FLAG,
      "Tells the browser to rewrite http:// subresource requests to https://.",
      CSP_RISK_LOW, CSP_SRC_EMPTY },
    { "block-all-mixed-content", CSP_KIND_FLAG,
      "Blocks loading of any http:// subresource on an https:// page.",
      CSP_RISK_LOW, CSP_SRC_EMPTY },
};

static const size_t CSP_DIRECTIVE_COUNT =
    sizeof(CSP_DIRECTIVES) / sizeof(CSP_DIRECTIVES[0]);

/** Source presets offered in the UI when adding a source to a directive. */
static const char *const CSP_COMMON_SOURCES[] = {
    "'self'", "'none'", "'unsafe-inline'", "'unsafe-eval'",
    "'strict-dynamic'", "data:", "blob:", "https:", NULL
};

/* -------------------------------------------------------------------------
 * Policy map — an insertion-ordered vector of directive entries
 * ------------------------------------------------------------------------- */

typedef struct {
    char *name;            /* owned, lowercase */
    char **sources;        /* owned vector of owned strings */
    size_t source_count;
} csp_entry;

typedef struct {
    csp_entry *entries;
    size_t count;
    size_t capacity;
} csp_policy;

/** Initialize an empty policy. */
void csp_policy_init(csp_policy *policy)
{
    policy->entries = NULL;
    policy->count = 0;
    policy->capacity = 0;
}

/** Release every allocation owned by the policy. */
void csp_policy_free(csp_policy *policy)
{
    if (policy == NULL) return;
    for (size_t i = 0; i < policy->count; i++) {
        for (size_t j = 0; j < policy->entries[i].source_count; j++)
            free(policy->entries[i].sources[j]);
        free(policy->entries[i].sources);
        free(policy->entries[i].name);
    }
    free(policy->entries);
    csp_policy_init(policy);
}

/** strdup is POSIX, not C11 — keep the file freestanding on the C standard. */
static char *csp_strdup(const char *s)
{
    size_t len = strlen(s) + 1;
    char *copy = malloc(len);

    if (copy != NULL) memcpy(copy, s, len);
    return copy;
}

/** Lowercase copy of `s`. Caller frees. */
static char *csp_strdup_lower(const char *s)
{
    char *copy = csp_strdup(s);

    if (copy == NULL) return NULL;
    for (char *p = copy; *p != '\0'; p++)
        *p = (char)tolower((unsigned char)*p);
    return copy;
}

/** Look up a directive by exact (lowercase) name, or NULL. */
csp_entry *csp_policy_get(const csp_policy *policy, const char *name)
{
    for (size_t i = 0; i < policy->count; i++) {
        if (strcmp(policy->entries[i].name, name) == 0)
            return &policy->entries[i];
    }
    return NULL;
}

/**
 * Set (or replace) a directive's source list. `sources` is a NULL-terminated
 * array of C strings; pass CSP_SRC_EMPTY for flag directives. Returns false
 * only on allocation failure.
 */
bool csp_policy_set(csp_policy *policy, const char *name,
                    const char *const *sources)
{
    char *lower = csp_strdup_lower(name);
    csp_entry *existing;
    size_t n = 0;
    char **vec;

    if (lower == NULL) return false;

    while (sources != NULL && sources[n] != NULL) n++;

    vec = (n > 0) ? calloc(n, sizeof(*vec)) : NULL;
    if (n > 0 && vec == NULL) {
        free(lower);
        return false;
    }
    for (size_t i = 0; i < n; i++) {
        vec[i] = csp_strdup(sources[i]);
        if (vec[i] == NULL) {
            for (size_t j = 0; j < i; j++) free(vec[j]);
            free(vec);
            free(lower);
            return false;
        }
    }

    existing = csp_policy_get(policy, lower);
    if (existing != NULL) {
        for (size_t j = 0; j < existing->source_count; j++)
            free(existing->sources[j]);
        free(existing->sources);
        existing->sources = vec;
        existing->source_count = n;
        free(lower);
        return true;
    }

    if (policy->count == policy->capacity) {
        size_t cap = (policy->capacity == 0) ? 8 : policy->capacity * 2;
        csp_entry *grown = realloc(policy->entries, cap * sizeof(*grown));

        if (grown == NULL) {
            for (size_t j = 0; j < n; j++) free(vec[j]);
            free(vec);
            free(lower);
            return false;
        }
        policy->entries = grown;
        policy->capacity = cap;
    }

    policy->entries[policy->count].name = lower;
    policy->entries[policy->count].sources = vec;
    policy->entries[policy->count].source_count = n;
    policy->count++;
    return true;
}

/* -------------------------------------------------------------------------
 * A growable string, so build() can concatenate without size guessing
 * ------------------------------------------------------------------------- */

typedef struct {
    char *data;
    size_t len;
    size_t capacity;
} csp_buffer;

static bool csp_buffer_append(csp_buffer *buf, const char *text)
{
    size_t add = strlen(text);

    if (buf->len + add + 1 > buf->capacity) {
        size_t cap = (buf->capacity == 0) ? 128 : buf->capacity;
        char *grown;

        while (buf->len + add + 1 > cap) cap *= 2;
        grown = realloc(buf->data, cap);
        if (grown == NULL) return false;
        buf->data = grown;
        buf->capacity = cap;
    }
    memcpy(buf->data + buf->len, text, add + 1);
    buf->len += add;
    return true;
}

/* -------------------------------------------------------------------------
 * Catalog predicates
 * ------------------------------------------------------------------------- */

/** True when the directive takes no value — emitted as a bare name. */
static bool csp_is_flag_directive(const char *name)
{
    for (size_t i = 0; i < CSP_DIRECTIVE_COUNT; i++) {
        if (CSP_DIRECTIVES[i].kind == CSP_KIND_FLAG &&
            strcmp(CSP_DIRECTIVES[i].name, name) == 0)
            return true;
    }
    return false;
}

/** True when the directive appears in the built-in catalog. */
static bool csp_is_known_directive(const char *name)
{
    for (size_t i = 0; i < CSP_DIRECTIVE_COUNT; i++) {
        if (strcmp(CSP_DIRECTIVES[i].name, name) == 0) return true;
    }
    return false;
}

/* -------------------------------------------------------------------------
 * build
 * ------------------------------------------------------------------------- */

static bool csp_emit(csp_buffer *buf, const csp_policy *policy,
                     const char *name, bool *first)
{
    const csp_entry *entry = csp_policy_get(policy, name);

    if (entry == NULL) return true;

    if (!csp_is_flag_directive(name) && entry->source_count == 0) return true;

    if (!*first && !csp_buffer_append(buf, "; ")) return false;
    *first = false;

    if (!csp_buffer_append(buf, name)) return false;
    if (csp_is_flag_directive(name)) return true;

    for (size_t i = 0; i < entry->source_count; i++) {
        if (!csp_buffer_append(buf, " ")) return false;
        if (!csp_buffer_append(buf, entry->sources[i])) return false;
    }
    return true;
}

/**
 * Assemble a policy map into the `Content-Security-Policy` header value.
 * Known directives emit in catalog order, unknown directives after them in
 * insertion order. Flag directives emit as a bare name; source/url directives
 * with an empty list are omitted (a valueless directive is invalid CSP).
 * An empty map yields an empty string. Caller frees the result.
 */
char *csp_build(const csp_policy *policy)
{
    csp_buffer buf = { NULL, 0, 0 };
    bool first = true;

    if (!csp_buffer_append(&buf, "")) return NULL;

    for (size_t i = 0; i < CSP_DIRECTIVE_COUNT; i++) {
        if (!csp_emit(&buf, policy, CSP_DIRECTIVES[i].name, &first)) {
            free(buf.data);
            return NULL;
        }
    }
    for (size_t i = 0; i < policy->count; i++) {
        if (csp_is_known_directive(policy->entries[i].name)) continue;
        if (!csp_emit(&buf, policy, policy->entries[i].name, &first)) {
            free(buf.data);
            return NULL;
        }
    }
    return buf.data;
}

/* -------------------------------------------------------------------------
 * parse
 * ------------------------------------------------------------------------- */

/**
 * Parse a CSP header value back into a policy map. Lenient: splits on
 * semicolons and whitespace, lowercases directive names, ignores empty tokens,
 * and strips an optional leading `Content-Security-Policy:` label so a pasted
 * full header line works. Duplicate directives keep only the first occurrence
 * (matching how browsers honor them). Never fails on content; garbage in,
 * empty policy out. Returns false only on allocation failure.
 */
bool csp_parse(const char *header, csp_policy *policy)
{
    static const char LABEL[] = "content-security-policy";
    const char *text, *end, *cursor;
    char *lowered;

    csp_policy_init(policy);
    if (header == NULL) return true;

    text = header;
    while (*text != '\0' && isspace((unsigned char)*text)) text++;
    end = text + strlen(text);
    while (end > text && isspace((unsigned char)end[-1])) end--;

    /* Strip an optional `Content-Security-Policy :` label (case-insensitive). */
    lowered = csp_strdup_lower(text);
    if (lowered == NULL) return false;
    if (strncmp(lowered, LABEL, sizeof(LABEL) - 1) == 0) {
        const char *after = text + sizeof(LABEL) - 1;

        while (after < end && isspace((unsigned char)*after)) after++;
        if (after < end && *after == ':') text = after + 1;
    }
    free(lowered);

    cursor = text;
    while (cursor < end) {
        const char *semi = memchr(cursor, ';', (size_t)(end - cursor));
        const char *token_end = (semi != NULL) ? semi : end;
        const char *word = cursor;
        bool is_name = true;
        char *name = NULL;

        while (word < token_end) {
            const char *word_end;
            size_t word_len;
            char *value;

            while (word < token_end && isspace((unsigned char)*word)) word++;
            if (word >= token_end) break;

            word_end = word;
            while (word_end < token_end && !isspace((unsigned char)*word_end))
                word_end++;
            word_len = (size_t)(word_end - word);

            value = malloc(word_len + 1);
            if (value == NULL) {
                free(name);
                csp_policy_free(policy);
                return false;
            }
            memcpy(value, word, word_len);
            value[word_len] = '\0';

            if (is_name) {
                name = csp_strdup_lower(value);
                free(value);
                if (name == NULL) {
                    csp_policy_free(policy);
                    return false;
                }
                is_name = false;
                /* Duplicate directive: browsers honor the first occurrence. */
                if (csp_policy_get(policy, name) != NULL) {
                    free(name);
                    name = NULL;
                    break;
                }
                if (!csp_policy_set(policy, name, CSP_SRC_EMPTY)) {
                    free(name);
                    csp_policy_free(policy);
                    return false;
                }
            } else {
                csp_entry *entry = csp_policy_get(policy, name);
                char **grown = realloc(entry->sources,
                                       (entry->source_count + 1) *
                                           sizeof(*grown));

                if (grown == NULL) {
                    free(value);
                    free(name);
                    csp_policy_free(policy);
                    return false;
                }
                entry->sources = grown;
                entry->sources[entry->source_count++] = value;
            }
            word = word_end;
        }

        free(name);
        cursor = (semi != NULL) ? semi + 1 : end;
    }
    return true;
}

/* -------------------------------------------------------------------------
 * Risk analysis
 * ------------------------------------------------------------------------- */

/** Sources treated as security-weakening, compared case-insensitively. */
static const char *const CSP_RISKY_SOURCES[] = {
    "'unsafe-inline'", "'unsafe-eval'", "data:", "http:", "*", NULL
};

/** Trimmed + lowercased copy of `source`. Caller frees. */
static char *csp_normalize_source(const char *source)
{
    const char *start = source, *end;
    size_t len;
    char *out;

    while (*start != '\0' && isspace((unsigned char)*start)) start++;
    end = start + strlen(start);
    while (end > start && isspace((unsigned char)end[-1])) end--;

    len = (size_t)(end - start);
    out = malloc(len + 1);
    if (out == NULL) return NULL;
    for (size_t i = 0; i < len; i++)
        out[i] = (char)tolower((unsigned char)start[i]);
    out[len] = '\0';
    return out;
}

/**
 * True when a source weakens the policy: 'unsafe-inline', 'unsafe-eval',
 * 'data:', 'http:', the bare wildcard '*', or any insecure http:// URL.
 * 'self', 'none', 'strict-dynamic', 'blob:', 'https:' and https URLs are fine.
 */
bool csp_is_risky_source(const char *source)
{
    char *s = csp_normalize_source(source);
    bool risky = false;

    if (s == NULL) return false;
    for (size_t i = 0; CSP_RISKY_SOURCES[i] != NULL; i++) {
        if (strcmp(s, CSP_RISKY_SOURCES[i]) == 0) {
            risky = true;
            break;
        }
    }
    if (!risky) risky = (strncmp(s, "http://", 7) == 0);
    free(s);
    return risky;
}

/** Short human explanation for each risky source (tooltip text in the UI). */
static const char *csp_lookup_explanation(const char *normalized)
{
    if (strcmp(normalized, "'unsafe-inline'") == 0)
        return "Allows inline <script>/<style> and event handlers - defeats "
               "most of CSP's XSS protection.";
    if (strcmp(normalized, "'unsafe-eval'") == 0)
        return "Allows eval() and similar code execution - weakens XSS "
               "protection.";
    if (strcmp(normalized, "*") == 0)
        return "Allows every origin - effectively no restriction for this "
               "directive.";
    if (strcmp(normalized, "data:") == 0)
        return "data: URIs can carry arbitrary payloads and are same-origin - "
               "attackers can smuggle content through them.";
    if (strcmp(normalized, "http:") == 0)
        return "Allows insecure origins - a network attacker can inject or "
               "tamper with subresources.";
    return NULL;
}

/** Explanation for any risky source; falls back to the generic insecure-origin text. */
const char *csp_risk_explanation(const char *source)
{
    char *key = csp_normalize_source(source);
    const char *found;

    if (key == NULL)
        return "Insecure http:// URL - traffic can be tampered with in transit.";
    found = csp_lookup_explanation(key);
    free(key);
    return (found != NULL)
               ? found
               : "Insecure http:// URL - traffic can be tampered with in transit.";
}

/* -------------------------------------------------------------------------
 * validate
 * ------------------------------------------------------------------------- */

/** One policy problem: either policy-wide (directive is "") or a risky source. */
typedef struct {
    char *directive;  /* owned; "" for policy-wide issues */
    char *source;     /* owned; NULL for policy-wide issues */
    char *message;    /* owned */
} csp_issue;

typedef struct {
    csp_issue *items;
    size_t count;
    size_t capacity;
} csp_issue_list;

void csp_issue_list_free(csp_issue_list *list)
{
    if (list == NULL) return;
    for (size_t i = 0; i < list->count; i++) {
        free(list->items[i].directive);
        free(list->items[i].source);
        free(list->items[i].message);
    }
    free(list->items);
    list->items = NULL;
    list->count = 0;
    list->capacity = 0;
}

static bool csp_issue_push(csp_issue_list *list, const char *directive,
                           const char *source, const char *message)
{
    if (list->count == list->capacity) {
        size_t cap = (list->capacity == 0) ? 8 : list->capacity * 2;
        csp_issue *grown = realloc(list->items, cap * sizeof(*grown));

        if (grown == NULL) return false;
        list->items = grown;
        list->capacity = cap;
    }

    list->items[list->count].directive = csp_strdup(directive);
    list->items[list->count].source = (source != NULL) ? csp_strdup(source) : NULL;
    list->items[list->count].message = csp_strdup(message);

    if (list->items[list->count].directive == NULL ||
        list->items[list->count].message == NULL ||
        (source != NULL && list->items[list->count].source == NULL)) {
        free(list->items[list->count].directive);
        free(list->items[list->count].source);
        free(list->items[list->count].message);
        return false;
    }
    list->count++;
    return true;
}

/**
 * Lint a policy: warns when default-src is missing (unset directives fall back
 * to the browser's allow-everything default) and flags every risky source.
 * Returns false only on allocation failure.
 */
bool csp_validate(const csp_policy *policy, csp_issue_list *issues)
{
    issues->items = NULL;
    issues->count = 0;
    issues->capacity = 0;

    if (csp_policy_get(policy, "default-src") == NULL) {
        if (!csp_issue_push(issues, "", NULL,
                            "No default-src - every directive you don't set "
                            "explicitly falls back to the browser's permissive "
                            "default."))
            goto fail;
    }

    for (size_t i = 0; i < policy->count; i++) {
        const csp_entry *entry = &policy->entries[i];

        for (size_t j = 0; j < entry->source_count; j++) {
            const char *src = entry->sources[j];
            char message[512];

            if (!csp_is_risky_source(src)) continue;

            snprintf(message, sizeof(message), "%s: %s weakens this policy - %s",
                     entry->name, src, csp_risk_explanation(src));
            if (!csp_issue_push(issues, entry->name, src, message)) goto fail;
        }
    }
    return true;

fail:
    csp_issue_list_free(issues);
    return false;
}

/* -------------------------------------------------------------------------
 * securityScore
 * ------------------------------------------------------------------------- */

/** Score penalty per risky source (case-insensitive key). */
static int csp_score_penalty(const char *normalized)
{
    if (strcmp(normalized, "'unsafe-inline'") == 0) return 20;
    if (strcmp(normalized, "'unsafe-eval'") == 0) return 15;
    if (strcmp(normalized, "*") == 0) return 20;
    if (strcmp(normalized, "data:") == 0) return 10;
    if (strcmp(normalized, "http:") == 0) return 10;
    return (strncmp(normalized, "http://", 7) == 0) ? 10 : 0;
}

/**
 * Security score, 0-100. Starts at 100; each risky source subtracts its
 * penalty (insecure http:// URLs subtract 10), and a missing default-src
 * subtracts 10. Clamped to 0-100. Deterministic.
 */
int csp_security_score(const csp_policy *policy)
{
    int score = 100;

    if (csp_policy_get(policy, "default-src") == NULL) score -= 10;

    for (size_t i = 0; i < policy->count; i++) {
        const csp_entry *entry = &policy->entries[i];

        for (size_t j = 0; j < entry->source_count; j++) {
            char *s = csp_normalize_source(entry->sources[j]);

            if (s == NULL) continue;
            score -= csp_score_penalty(s);
            free(s);
        }
    }

    if (score < 0) score = 0;
    if (score > 100) score = 100;
    return score;
}

/* -------------------------------------------------------------------------
 * Demo
 * ------------------------------------------------------------------------- */

int main(void)
{
    static const char *const script_sources[] = {
        "'self'", "'unsafe-inline'", "https://cdn.example.com", NULL
    };
    csp_policy policy;
    csp_issue_list issues;
    char *header;

    csp_policy_init(&policy);
    csp_policy_set(&policy, "default-src", CSP_SRC_SELF);
    csp_policy_set(&policy, "script-src", script_sources);
    csp_policy_set(&policy, "object-src", CSP_SRC_NONE);
    csp_policy_set(&policy, "upgrade-insecure-requests", CSP_SRC_EMPTY);

    header = csp_build(&policy);
    printf("header: %s\n", (header != NULL) ? header : "(allocation failed)");
    printf("score:  %d/100\n", csp_security_score(&policy));

    if (csp_validate(&policy, &issues)) {
        for (size_t i = 0; i < issues.count; i++)
            printf("issue:  %s\n", issues.items[i].message);
        csp_issue_list_free(&issues);
    }

    free(header);
    csp_policy_free(&policy);

    /* Round-trip a pasted header line. */
    if (csp_parse("Content-Security-Policy: default-src 'self'; img-src *",
                  &policy)) {
        char *rebuilt = csp_build(&policy);

        printf("parsed: %s\n", (rebuilt != NULL) ? rebuilt : "(allocation failed)");
        free(rebuilt);
        csp_policy_free(&policy);
    }

    (void)CSP_COMMON_SOURCES;
    return 0;
}

Also available in 8 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →