Document the PHP api layer and fix what documenting it exposed

Writing down what each endpoint actually guarantees made the places it
didn't obvious — shell arguments reaching a crontab or a bash -c
unescaped, master.conf written without tmp+rename, and conf edits that
could be saved without ever being parsed.
This commit is contained in:
Gmer4Lfe
2026-08-02 10:11:39 -04:00
parent 6a959fb5e4
commit 987313e7dc
55 changed files with 3972 additions and 95 deletions
+57 -4
View File
@@ -1,8 +1,61 @@
<?php
// Partnership-related settings for this host — the Partnership section of each accessible conf.
// HOST1 (owner): master.conf PARTNERSHIP + host1.conf Partnership. Other hosts: their own.
// Uses vv_conf_all_groups() (handles master.conf's sandwiched major header) then filters
// to partnership sections. Writes go through confform.php (which pushes master.conf to partners).
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// PURPOSE
// Partnership settings reader. Returns the partnership-related field groups from every conf
// file this host is allowed to see, so the partnership tab can render them as a form.
//
// OPERATIONAL MODEL
// Read-only, and deliberately so. Saves from this form go to confform.php, which already
// knows how to write a field change and push master.conf to partners. Duplicating that here
// would mean two write paths for the same keys, and only one of them would push.
//
// Which files are read is decided by the host, not the request. On HOST1 that is
// master.conf's PARTNERSHIP section plus host1.conf's; on any other host, its own conf
// alone — the same split sparse checkout enforces at git level.
//
// DESIGN PRINCIPLES
// Filters full group parsing rather than pattern-matching the file.
// vv_conf_all_groups() is used and then filtered by subsection, because it already
// handles master.conf's sandwiched major header — the structure a naive section grep
// gets wrong.
//
// Matches the section name case-insensitively and by substring.
// stripos, not equality, because the section is spelled PARTNERSHIP in master.conf and
// Partnership in the host confs. Requiring an exact match would silently return one
// file's groups and not the other's.
//
// Files with no partnership section are omitted entirely.
// The response lists only files that contributed, so the page renders one panel per
// real section rather than an empty panel per readable file.
//
// OPERATIONAL SAFEGUARDS
// Read-only. No parameters, no writes, nothing to validate — there is no input to this
// endpoint at all, which is what makes the file list unforgeable.
//
// The file list comes from vv_get_conf_files(), the same allowlist every conf endpoint uses,
// so this cannot expose a partner's conf even though it enumerates rather than being told
// what to read.
//
// Never cached.
// Cache-Control: no-store, no-cache — these are the values a user is actively editing,
// and a cached read would show them their own change reverting.
//
// Credential fields are returned as they appear in the conf.
// The partnership sections carry SSH key paths and API keys, and this endpoint returns
// them for editing. That is the same exposure the raw conf editor has, over the same
// WebGUI session; see the CSRF note in README-unraid.md.
//
// REQUEST
// GET, no parameters
//
// RESPONSE
// {"ok":true,"files":[{"file":"<conf>","groups":[…]}, …]}
//
// DEPENDS ON
// include/confform.php vv_conf_all_groups()
// include/config.php vv_get_conf_files()
// api/confform.php the write path for these same fields
// ═══════════════════════════════════════════════════════════════════════════════════════════════
header('Content-Type: application/json');
header('Cache-Control: no-store, no-cache');
require_once dirname(__DIR__) . '/include/confform.php';