- Purge misplaced RFCs, scratch tokens, and node-proprietary assets - Decouple remote provisioning to ci/scripts/init-node.py - Add targeted composition orchestration in actions/compose.py - Add host-side Xray stale socket cleanup in /dev/shm - Add actions/env.py for shell exports and .env synchronization - Harden UFW, backup retention, host tempfiles, and notify dispatcher - Add pyproject.toml and Makefile with 78 passing unit tests
30 lines
1.2 KiB
Python
30 lines
1.2 KiB
Python
"""Target-only decoding; importing the CLI never requires a YAML dependency."""
|
|
from .contract import ContractError, normalize_user_data
|
|
|
|
|
|
def decode_user_data(content):
|
|
import yaml
|
|
|
|
class Loader(yaml.SafeLoader):
|
|
def compose_node(self, parent, index):
|
|
if self.check_event(yaml.AliasEvent):
|
|
raise ContractError('YAML aliases are not supported')
|
|
return super().compose_node(parent, index)
|
|
|
|
def construct_mapping(self, node, deep=False):
|
|
result = {}
|
|
for key_node, value_node in node.value:
|
|
key = self.construct_object(key_node, deep=deep)
|
|
if not isinstance(key, str) or key in result:
|
|
raise ContractError('YAML mapping keys must be unique strings')
|
|
result[key] = self.construct_object(value_node, deep=deep)
|
|
return result
|
|
|
|
if len(content) > 1024 * 1024:
|
|
raise ContractError('user-data exceeds the supported size')
|
|
try:
|
|
document = yaml.load(content, Loader=Loader)
|
|
except (yaml.YAMLError, UnicodeError, RecursionError):
|
|
raise ContractError('invalid or unsupported YAML user-data') from None
|
|
return normalize_user_data(document)
|