FEATURE SPOTLIGHT: File-Based Rule Replacements

This document describes how to keep sensitive values (passwords, API keys, tokens) out of FUZZY_MAP_pre / FUZZY_MAP source code and Git history by loading the replacement text from a separate file at runtime instead of hardcoding it.

This is especially useful during livestreams or screen shares, where the map source code itself may be visible, but the referenced file is not.


1. The Concept

Normally, the replacement field of a rule is the literal output text:

('my-secret-value', r'^(trigger)$', 85, {'command_flags': re.IGNORECASE})

With file-based replacement enabled, a replacement value that starts with a configured prefix (by default - or .) is instead treated as a filename. Aura resolves that filename relative to the plugin’s own directory, reads its content, and uses that content as the replacement text.

('-api_key.txt', r'^(show api key)$', 85, {'command_flags': re.IGNORECASE})

If api_key.txt exists next to the plugin’s FUZZY_MAP_pre.py, its (stripped) content is used as the replacement. If the file does not exist, the literal string -api_key.txt is returned instead (fail-safe: no accidental leakage of “file not found” as usable text, and no crash).


2. Settings

Configured in config/settings.py (or config/settings_local.py for local overrides):

Setting

Type

Default

Description

FILE4REPLACEMENT_USE

bool

True

Master switch for the whole feature. If False, replacement is always used literally.

FILE4REPLACEMENT_ALLOWED_PREFIXES

tuple[str]

('-', '.')

replacement values must start with one of these prefixes to trigger a file lookup. Empty/None = any value not starting with a letter is treated as a potential filename.

FILE4REPLACEMENT_ALLOW_PATH_TRAVERSAL

bool

False

If True, allows resolving files outside the plugin’s own directory (e.g. absolute paths, or ../ sequences). See Security section below.

FILE4REPLACEMENT_DENY_PREFIXES

tuple[str]

e.g. ('/etc', '/proc', '/dev', '/var/lib', '/root', 'C:\\Windows', 'C:\\Program Files')

Resolved absolute paths starting with any of these are always rejected, regardless of FILE4REPLACEMENT_ALLOW_PATH_TRAVERSAL. Hard security boundary against system directories.


3. Path Resolution

The file is resolved as follows:

  1. The plugin’s source_path (recorded automatically by the map loader) is joined against SL5NET_AURA_PROJECT_ROOT (read from the SL5NET_AURA_PROJECT_ROOT environment variable) to get the plugin’s directory.

  2. The replacement value is joined onto that directory.

  3. Unless FILE4REPLACEMENT_ALLOW_PATH_TRAVERSAL is True, the resolved path must stay inside the plugin’s directory, or the lookup is rejected.

  4. Regardless of the above, any resolved path starting with an entry in FILE4REPLACEMENT_DENY_PREFIXES is always rejected.

  5. If the file exists, its stripped content is returned. Otherwise, the original replacement string is returned unchanged.


4. Security Notes

  • Only enable FILE4REPLACEMENT_ALLOW_PATH_TRAVERSAL if you understand the implications: it allows any user who can edit a FUZZY_MAP_pre file (e.g. via an online map editor) to read arbitrary files that the Aura process can access, and have their content surface as live output text.

  • FILE4REPLACEMENT_DENY_PREFIXES provides a baseline protection against common system directories even when path traversal is allowed, but it is not a substitute for restricting who can edit map files in the first place.

  • Referenced files are plain text on disk. Combine with your OS’s file permissions if the content is sensitive.


5. Example

See config/maps/plugins/TEST_FILE4REPLACEMENT/ for a working example plugin, and tools/tests/TEST_FILE4REPLACEMENT.sh for a test script that exercises both an in-directory lookup and a lookup outside the plugin directory.

# config/maps/plugins/TEST_FILE4REPLACEMENT/de-DE/FUZZY_MAP_pre.py
FUZZY_MAP_pre = [
    ('.Zebra.txt', r'^(Zebra)$', 85, {'command_flags': re.IGNORECASE}),
]

Create .Zebra.txt next to this file with the desired replacement text, then say (or type via the console) s Zebra to trigger it.