Security

Most of Hilde's safety comes from the language: a resource is used exactly once (linear types), a function states its contract, an index is checked, and bad input is answered with a refusal, not a crash. This page describes what the standard library adds and where its limits are.

"unsafe" only in the bindings

The language itself needs no unsafe code. A module is unsafe only when its body says pragma Unsafe_Module;, and only such a module may bind a C function (pragma Foreign_Import), paste C with @embed, or import Hilde.Memory, the built-in module of raw memory. The compiler checks this (H2028, H2038).

Sandbox

Standard.Sandbox confines the running process to what a policy allows. A policy names promises in the words of pledge and paths in the permission letters of unveil. Its report names each mechanism that took effect, and no other; nothing in it stops the programme, so what an unconfined run means is the programme's decision. The code was taken over and adapted from the sandbox written for the web server Hiawatha.

SystemWhat is applied
OpenBSDthe paths are unveiled, the view is locked, the promises are pledged, with execpromises
Linuxno new privileges, the paths as a Landlock ruleset, a seccomp filter; the promises are checked, not enforced
macOSthe programme starts again under a Seatbelt profile of the paths; the promises allow the network, fork and exec as a whole, the rest are not enforced
FreeBSDthe paths are opened with their rights and the process enters Capsicum's capability mode; only with promises without network or exec, otherwise nothing is applied

On Android, on other systems and in a library nothing is applied, and the report says so.

A complete programme: it promises standard output and reading, allows the working directory for reading, applies the policy and prints the report. It was checked with hilde check, compiled and run on Linux for this page:

File: SandboxPolicy.hiq

import Standard.String (String, asSpan, destroyString);
import Standard.Sandbox (Policy, makePolicy, allowPromises, allowPath, Report, applyPolicy, describeReport);

module body Example.SandboxPolicy is
    function main(): ExitCode is
        let promised: Policy := allowPromises(makePolicy(), "stdio rpath");
        let policy: Policy := allowPath(promised, ".", "r");
        let report: Report := applyPolicy(policy);
        let line: String := describeReport(report);
        printLn(asSpan(&line));
        destroyString(line);
        return ExitSuccess();
    end;
end module body.

Output of hilde check SandboxPolicy.hiq --modules=standard/src (exit status 0):

hilde check: 1 modules given, 6 checked, no error

Output of ./main (exit status 0):

sandbox:     no-new-privileges, landlock abi 10, 1 of 1 paths, seccomp
Source: example written for this page, hilde 1.0.0

Signatures: ML-DSA-44 with Ed25519

Standard.MlDsa provides ML-DSA of FIPS 204 at the levels 44, 65 and 87, from the C code of libcrux that OpenSSH carries, the same on every system. Standard.CompositeSig is the composite signature ML-DSA-44 with Ed25519 after the IETF draft: it is valid only when both signatures hold, so a break of one method alone forges nothing. RAMBO signs its tokens with ML-DSA-44, or with the composite where a client asks for it.

Vault

Standard.Vault keeps secrets in an encrypted .env file: XChaCha20-Poly1305 and HMAC-SHA3-512, the key made from a key and a pepper of exactly 32 bytes, the pepper kept in a file of its own. The file is written atomically with mode 0600, owner and group are kept, key and pepper can be rotated, and the key material is a linear value, wiped when it is closed. The format is compatible with the vault of the Go library Hilde took as its model.

Sanitizer

sanitize in Standard.Html keeps only what a policy allows: code, frames and forms fall away, URLs are allowed only with http, https and mailto, and the output is always well formed. Wolly.Markdown sends its own HTML through it once more.

Sanitizers of another kind check the generated C: hilde test --memory runs the tests under AddressSanitizer and UndefinedBehaviorSanitizer, --race under ThreadSanitizer, and the build checks run them on every suite.

More in the standard library

Packages

Packages and releases are signed with signify; how to check them and where the public keys lie is on the packages page.