ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_sanitize.c File Reference

Implementation of the untrusted-name sanitisers. More...

#include "mdl_sanitize.h"
#include <stdio.h>
#include <string.h>
#include "ra8_attributes.h"
Include dependency graph for mdl_sanitize.c:

Go to the source code of this file.

Enumerations

enum  mdl_sanitize_size_t : uint8_t {
  k_reserved_base_max = 8 ,
  k_reserved_len = 4 ,
  k_reserved_digit_at = 3
}
 Small fixed sizes used while sanitising a name. More...

Functions

static char internal_lower_ascii (char c)
 ASCII lower-case of one character (locale-independent).
static bool internal_is_allowed_char (char c)
 True if c may appear verbatim in a sanitised segment.
static bool internal_is_dot_segment (const char *name)
 True if name is empty, ".", or ".." (no useful segment).
static void internal_base_of (const char *name, char *base, size_t cap)
 Case-folded base name (up to the first ".") of name into base.
static bool internal_is_reserved_base (const char *name)
 True if name's base is a Windows reserved device name.
static bool internal_copy_sanitised (const char *raw, char *out, size_t cap, size_t *out_len)
 Copy raw into out replacing unsafe bytes; report bad/truncated.
static void internal_prepend_underscore (char *out, size_t cap, size_t len)
 Prepend _ to out in place, staying within cap.
bool mdl_sanitize_segment (const char *raw, char *out, size_t cap)
 Rewrite an untrusted path segment into a safe single filename.
bool mdl_path_contained (const char *parent, const char *candidate)
 True when candidate is lexically contained under parent.
static bool internal_has_separator (const char *seg)
 True if seg embeds a path separator (would span directories).
bool mdl_path_join (const char *parent, const char *seg, char *out, size_t cap)
 Join one safe child segment under a parent directory path.
static const char * internal_xml_entity (char c)
 XML entity for a metacharacter, or NULL when c needs no escape.
bool mdl_xml_escape (const char *src, char *out, size_t cap)
 XML-escape src into out, failing rather than truncating.

Variables

static const char *const s_fallback_name = "item"
 Fallback name substituted when a segment sanitises to nothing.
static const char *const s_reserved_exact [] = {"con", "prn", "aux", "nul"}
 Exactly-reserved Windows device base names (case-folded).
static const char *const s_reserved_numbered [] = {"com", "lpt"}
 Reserved Windows device prefixes taking a 1-9 suffix.

Detailed Description

Implementation of the untrusted-name sanitisers.

Converts untrusted title and URL segments into bounded portable names while rejecting separators and special path components.

Definition in file mdl_sanitize.c.

Enumeration Type Documentation

◆ mdl_sanitize_size_t

enum mdl_sanitize_size_t : uint8_t

Small fixed sizes used while sanitising a name.

Enumerator
k_reserved_base_max 

Buffer for a Windows reserved-name base.

k_reserved_len 

Length of a COMx / LPTx reserved name.

k_reserved_digit_at 

Index of the digit in COMx / LPTx.

Definition at line 17 of file mdl_sanitize.c.

Function Documentation

◆ internal_base_of()

void internal_base_of ( const char * name,
char * base,
size_t cap )
static

Case-folded base name (up to the first ".") of name into base.

Uses locale-independent ASCII rules and supplied destination bounds. Every write remains within capacity and leaves a NUL-terminated result.

Parameters
[in]nameNUL-terminated name or path segment.
[in,out]baseNUL-terminated base URL or path.
[in]capDestination capacity including any terminator.
Precondition
Every required pointer is non-null and remains valid for the call.
Lengths and capacities describe complete referenced objects without overflow.
Postcondition
Documented outputs and the return value describe the same outcome.
A rejected or failed operation is never reported as successful.
Note
Thread safety follows ownership of the supplied context; no synchronization is added.
Since
Version 0.1.0

Definition at line 102 of file mdl_sanitize.c.

References internal_lower_ascii(), and RA8_INTERNAL.

Referenced by internal_is_reserved_base().

◆ internal_copy_sanitised()

bool internal_copy_sanitised ( const char * raw,
char * out,
size_t cap,
size_t * out_len )
static

Copy raw into out replacing unsafe bytes; report bad/truncated.

Uses locale-independent ASCII rules and supplied destination bounds. Every write remains within capacity and leaves a NUL-terminated result.

Parameters
[in]rawUntrusted source text to validate or resolve.
[out]outCaller-owned result storage.
[in]capDestination capacity including any terminator.
[out]out_lenReceives the produced byte length.
Returns
True only when no input byte was replaced or truncated.
Return values
trueThe documented predicate holds or the requested operation completed.
falseThe predicate does not hold or validation rejected the operation.
Precondition
Every required pointer is non-null and remains valid for the call.
Lengths and capacities describe complete referenced objects without overflow.
Postcondition
Documented outputs and the return value describe the same outcome.
A rejected or failed operation is never reported as successful.
Note
Thread safety follows ownership of the supplied context; no synchronization is added.
Since
Version 0.1.0

Definition at line 164 of file mdl_sanitize.c.

References internal_is_allowed_char().

Referenced by mdl_sanitize_segment().

◆ internal_has_separator()

bool internal_has_separator ( const char * seg)
static

True if seg embeds a path separator (would span directories).

Uses locale-independent ASCII rules and supplied destination bounds. Every write remains within capacity and leaves a NUL-terminated result.

Parameters
[in]segNUL-terminated path segment.
Returns
True when seg contains a directory separator.
Return values
trueThe documented predicate holds or the requested operation completed.
falseThe predicate does not hold or validation rejected the operation.
Precondition
Every required pointer is non-null and remains valid for the call.
Lengths and capacities describe complete referenced objects without overflow.
Postcondition
Documented outputs and the return value describe the same outcome.
A rejected or failed operation is never reported as successful.
Note
Thread safety follows ownership of the supplied context; no synchronization is added.
Since
Version 0.1.0

Definition at line 264 of file mdl_sanitize.c.

References RA8_INTERNAL, and strchr().

Referenced by mdl_path_join().

◆ internal_is_allowed_char()

bool internal_is_allowed_char ( char c)
static

True if c may appear verbatim in a sanitised segment.

Uses locale-independent ASCII rules and supplied destination bounds. Every write remains within capacity and leaves a NUL-terminated result.

Parameters
[in]cInput ASCII byte.
Returns
True when c is an ASCII letter, digit, dot, dash, or underscore.
Return values
trueThe documented predicate holds or the requested operation completed.
falseThe predicate does not hold or validation rejected the operation.
Precondition
Every required pointer is non-null and remains valid for the call.
Lengths and capacities describe complete referenced objects without overflow.
Postcondition
Documented outputs and the return value describe the same outcome.
A rejected or failed operation is never reported as successful.
Note
Thread safety follows ownership of the supplied context; no synchronization is added.
Since
Version 0.1.0

Definition at line 64 of file mdl_sanitize.c.

References RA8_INTERNAL.

Referenced by internal_copy_sanitised().

◆ internal_is_dot_segment()

bool internal_is_dot_segment ( const char * name)
static

True if name is empty, ".", or ".." (no useful segment).

Uses locale-independent ASCII rules and supplied destination bounds. Every write remains within capacity and leaves a NUL-terminated result.

Parameters
[in]nameNUL-terminated name or path segment.
Returns
True when name is empty, . or ..; otherwise false.
Return values
trueThe documented predicate holds or the requested operation completed.
falseThe predicate does not hold or validation rejected the operation.
Precondition
Every required pointer is non-null and remains valid for the call.
Lengths and capacities describe complete referenced objects without overflow.
Postcondition
Documented outputs and the return value describe the same outcome.
A rejected or failed operation is never reported as successful.
Note
Thread safety follows ownership of the supplied context; no synchronization is added.
Since
Version 0.1.0

Definition at line 84 of file mdl_sanitize.c.

References RA8_INTERNAL, and strcmp().

Referenced by mdl_path_join(), and mdl_sanitize_segment().

◆ internal_is_reserved_base()

bool internal_is_reserved_base ( const char * name)
static

True if name's base is a Windows reserved device name.

Uses locale-independent ASCII rules and supplied destination bounds. Every write remains within capacity and leaves a NUL-terminated result.

Parameters
[in]nameNUL-terminated name or path segment.
Returns
True when name has a Windows-reserved device base.
Return values
trueThe documented predicate holds or the requested operation completed.
falseThe predicate does not hold or validation rejected the operation.
Precondition
Every required pointer is non-null and remains valid for the call.
Lengths and capacities describe complete referenced objects without overflow.
Postcondition
Documented outputs and the return value describe the same outcome.
A rejected or failed operation is never reported as successful.
Note
Thread safety follows ownership of the supplied context; no synchronization is added.
Since
Version 0.1.0

Definition at line 126 of file mdl_sanitize.c.

References internal_base_of(), k_reserved_base_max, k_reserved_digit_at, k_reserved_len, RA8_INTERNAL, s_reserved_exact, s_reserved_numbered, strcmp(), strlen(), and strncmp().

Referenced by mdl_sanitize_segment().

◆ internal_lower_ascii()

char internal_lower_ascii ( char c)
static

ASCII lower-case of one character (locale-independent).

Uses locale-independent ASCII rules and supplied destination bounds. Every write remains within capacity and leaves a NUL-terminated result.

Parameters
[in]cInput ASCII byte.
Returns
Lower-case ASCII mapping of c, or c unchanged outside A-Z.
Return values
0The input character was NUL.
otherLower-case mapping or unchanged input character.
Precondition
Every required pointer is non-null and remains valid for the call.
Lengths and capacities describe complete referenced objects without overflow.
Postcondition
Documented outputs and the return value describe the same outcome.
A rejected or failed operation is never reported as successful.
Note
Thread safety follows ownership of the supplied context; no synchronization is added.
Since
Version 0.1.0

Definition at line 45 of file mdl_sanitize.c.

References RA8_INTERNAL.

Referenced by internal_base_of().

◆ internal_prepend_underscore()

void internal_prepend_underscore ( char * out,
size_t cap,
size_t len )
static

Prepend _ to out in place, staying within cap.

Uses locale-independent ASCII rules and supplied destination bounds. Every write remains within capacity and leaves a NUL-terminated result.

Parameters
[out]outCaller-owned result storage.
[in]capDestination capacity including any terminator.
[in]lenReadable byte length.
Precondition
Every required pointer is non-null and remains valid for the call.
Lengths and capacities describe complete referenced objects without overflow.
Postcondition
Documented outputs and the return value describe the same outcome.
A rejected or failed operation is never reported as successful.
Note
Thread safety follows ownership of the supplied context; no synchronization is added.
Since
Version 0.1.0

Definition at line 202 of file mdl_sanitize.c.

References memmove(), and RA8_INTERNAL.

Referenced by mdl_sanitize_segment().

◆ internal_xml_entity()

const char * internal_xml_entity ( char c)
static

XML entity for a metacharacter, or NULL when c needs no escape.

Definition at line 295 of file mdl_sanitize.c.

References RA8_INTERNAL.

Referenced by mdl_xml_escape().

◆ mdl_path_contained()

bool mdl_path_contained ( const char * parent,
const char * candidate )

True when candidate is lexically contained under parent.

A prefix test that treats a directory boundary as significant, so /a/b contains /a/b and /a/b/c but not /a/bb. Both paths are expected to be already resolved (e.g. via realpath) so the comparison is purely lexical; trailing slashes on parent are ignored.

Parameters
[in]parentAbsolute, resolved parent directory, or NULL.
[in]candidateAbsolute, resolved candidate path, or NULL.
Returns
Whether candidate is parent itself or a descendant of it.
Return values
truecandidate equals parent or lies beneath it.
falseEither argument is NULL/empty, or candidate is outside.
Precondition
parent and candidate, when non-NULL, are NUL-terminated strings.
The caller resolved both paths before calling (no .. remains).
Postcondition
Neither argument is modified.
Note
Thread-safe: depends only on its arguments.
Since
0.1.0
Postcondition
Documented outputs and the return value describe the same outcome.

Definition at line 231 of file mdl_sanitize.c.

References strlen(), and strncmp().

◆ mdl_path_join()

bool mdl_path_join ( const char * parent,
const char * seg,
char * out,
size_t cap )

Join one safe child segment under a parent directory path.

The single join primitive every series_dir/chapter_dir join routes through, so a traversal name can never reach mkdir, an archiver, or an output path. It refuses – rather than composing – a seg that is not a single filesystem-safe segment: an empty string, . or .., or any name containing a / (which also rejects an absolute seg such as /etc). A result that would not fit out is likewise refused rather than truncated, since a silently-shortened path would name a different directory. seg is expected to already be the output of mdl_sanitize_segment (or a leaf composed from such a slug); this predicate is the defence-in-depth gate that makes an escape structurally impossible even if that upstream step regressed.

Parameters
[in]parentParent directory path (NUL-terminated), or NULL.
[in]segCandidate child segment (NUL-terminated), or NULL.
[out]outDestination buffer receiving parent/seg.
[in]capCapacity of out in bytes.
Returns
Whether out holds the joined parent/seg path.
Return values
trueout is parent + '/' + seg, NUL-terminated.
falseA NULL/zero argument, an unsafe seg, or a non-fitting result.
Precondition
out, when non-NULL, has room for at least cap bytes.
The caller treats false as a hard error (no partial path is used).
Postcondition
On false with cap > 0, out[0] is '\0' (no usable partial path).
On true, out contains exactly one / more than parent did.
Note
Thread-safe: writes only caller-provided storage.
See also
mdl_sanitize_segment Produces the safe seg this join expects.
mdl_path_contained Runtime realpath check callers pair with this.
Since
0.1.0

Definition at line 269 of file mdl_sanitize.c.

References internal_has_separator(), internal_is_dot_segment(), memcpy(), and strlen().

Referenced by internal_build_export_metadata(), internal_cache_paths(), internal_cover_current(), internal_download_page_image(), internal_init_site_identity(), internal_library_remove_child(), internal_library_visit(), internal_pack_combined_dir(), internal_prepare_artifact_path(), internal_remove_stale_page_variants(), internal_resolve_cache_path(), internal_resolve_descriptor_path(), internal_resolve_one_leaf(), internal_resolve_removal_target(), internal_verify_artifact_entry(), internal_verify_library_root(), mdl_app_run_verify(), mdl_join_dir_under(), mdl_pack_one_meta(), priv_mdl_app_ensure_series_cover(), priv_mdl_cache_publish_body(), and priv_mdl_cache_read_body().

◆ mdl_sanitize_segment()

bool mdl_sanitize_segment ( const char * raw,
char * out,
size_t cap )

Rewrite an untrusted path segment into a safe single filename.

Produces a non-empty NUL-terminated name in out that is always a single filesystem-safe segment: characters outside [A-Za-z0-9._-] (including /, NUL, and control bytes) become _, the traversal names . and .. and an empty result fall back to a generated name, a Windows reserved device name (CON, NUL, COM1..LPT9, ...) is prefixed with _, and any input longer than cap - 1 bytes is truncated. Because the result can contain no / and is never ./.., joining it under a parent directory cannot escape that directory.

Parameters
[in]rawUntrusted candidate name (NUL-terminated), or NULL.
[out]outDestination buffer for the sanitised name.
[in]capCapacity of out in bytes (must be >= 2 for a useful name).
Returns
Whether raw was already fully safe (copied verbatim).
Return values
trueout equals raw: no substitution, fallback, or truncation.
falseout was rewritten – the caller may log the change.
Precondition
out, when the call is useful, has room for at least cap bytes.
cap >= 2 so at least one character plus a NUL fits.
Postcondition
out is NUL-terminated and contains no /, .-only, or .. result.
out is non-empty whenever cap >= 2.
Note
Thread-safe: writes only caller-provided storage.
Since
0.1.0

Definition at line 213 of file mdl_sanitize.c.

References internal_copy_sanitised(), internal_is_dot_segment(), internal_is_reserved_base(), internal_prepend_underscore(), and s_fallback_name.

Referenced by internal_run_prepared(), and mdl_urlname_last_segment().

◆ mdl_xml_escape()

bool mdl_xml_escape ( const char * src,
char * out,
size_t cap )

XML-escape src into out, failing rather than truncating.

Replaces the five XML metacharacters (&, <, >, ", ') with their predefined entities and copies everything else verbatim. Applied to every untrusted filename interpolated into the OPF, nav document and per-page XHTML so a page named a"><script>.jpg cannot break the container's well-formedness. If the escaped result would not fit, the function fails instead of emitting a truncated (and possibly malformed) document.

Parameters
[in]srcSource string (NUL-terminated), or NULL.
[out]outDestination buffer for the escaped, NUL-terminated result.
[in]capCapacity of out in bytes.
Returns
Whether the fully escaped string fit in out.
Return values
trueout holds the complete escaped form of src.
falsesrc/out was NULL, cap was 0, or the result did not fit.
Precondition
out, when non-NULL, has room for at least cap bytes.
The caller treats false as a hard error (no partial output is used).
Postcondition
On false with cap > 0, out[0] is '\0'.
src is not modified.
Note
Thread-safe: writes only caller-provided storage.
Since
0.1.0

Definition at line 313 of file mdl_sanitize.c.

References internal_xml_entity(), memcpy(), and strlen().

Referenced by internal_epub_prepare_creators(), internal_epub_prepare_optional(), internal_epub_prepare_text(), internal_epub_write_page_xhtml(), and internal_escape_comicinfo().

Variable Documentation

◆ s_fallback_name

const char* const s_fallback_name = "item"
static

Fallback name substituted when a segment sanitises to nothing.

Definition at line 24 of file mdl_sanitize.c.

Referenced by mdl_sanitize_segment().

◆ s_reserved_exact

const char* const s_reserved_exact[] = {"con", "prn", "aux", "nul"}
static

Exactly-reserved Windows device base names (case-folded).

Definition at line 27 of file mdl_sanitize.c.

Referenced by internal_is_reserved_base().

◆ s_reserved_numbered

const char* const s_reserved_numbered[] = {"com", "lpt"}
static

Reserved Windows device prefixes taking a 1-9 suffix.

Definition at line 29 of file mdl_sanitize.c.

Referenced by internal_is_reserved_base().