# AGENTS.md — machine-facing rules for this repo **Read this before touching anything.** `README.md` explains the project to a human; this file states the rules an automated agent must follow. Where the two seem to disagree, this file wins for process questions. --- ## 1. What this repo is It produces **clean, deployable code baselines** for the Cadline web sites hosted on `tanis.cadline.hu`. A baseline serves two purposes at once: 1. the **known-good reference** the HIDS baseline-diff compares the live docroot against; 2. the **source of truth for deployment** — the tree that will be pushed to the server, replacing direct editing on the live host. The repo stores baseline **constituents plus a generator**, never the deployed tree. `build-baseline.sh` assembles `out/` deterministically; `out/` is git-ignored. ## 2. One site per branch There is **no per-site repo**. Sites and CMS versions are separated by **git branch**: | Branch | Site | CMS core | |---|---|---| | `main` | www.archline.hu | Joomla 3.8.11 | | `site/archlinexp.eu` | www.archlinexp.eu | Joomla 3.9.2 | `build-baseline.sh` is **site-agnostic**: it walks `core/`, `packages/`, `deployed/`, `cadline/`, `overrides/` and contains no site-specific logic. A new site means a new branch with different *content*, **not** a modified script. If you find yourself editing the generator to special-case a site, stop — that is the wrong branch of the design. Site branches are **parallel and long-lived**. They do **not** merge into `main`. Only repo-wide things (this file, `README.md`, the generator, `lib/`) belong on `main`. ## 3. The cardinal rule: never build a baseline from an infected tree Every one of these sites is or was compromised. A baseline built by copying the live docroot **would cement the malware into the reference and into every future deploy**. Therefore: - Take the CMS core from the **official upstream package**, never from live. - Take the site's own code from the **developer source repo** (`git.cadline.hu/cadline/websource`, per-site subdirectory), never from live. - Only for extensions with **no obtainable upstream** may files come from live — and then they are *vetted*, not trusted. - **The build output must be double-checked before it is used.** This is not optional: the archline baseline's first candidate contained 34 malware files, and only the double-check caught them. ## 4. Mandatory verification (do not skip, do not weaken) After every build, run all of these against `out/`: 1. **YARA** with the operator's ruleset. Expected: **zero** hits. 2. **IOC paths**: not one entry of `malware-iocs.paths` may exist under `out/`. 3. **Disguise sweep**: no `*.php.json`, `*.phar*`, `*.phtml*`, `_h3x_*`; no `.json` file containing a PHP open tag. 4. **Structural check** — the rule that catches what content matching misses: > An extra file on a **CMS core component path** that is **not** a template override > (`templates//html/...`) is **not a module**. It is a leftover or a plant. Diff `out/` against the vanilla package: for every path under a core component that the vanilla does not have, decide explicitly. This rule found a file-write webshell (`components/com_mailto/mail.php`) and an Akeeba Kickstart leftover carrying a password — **both invisible to YARA**. 5. **Core version**: assert the built `libraries/src/Version.php` is the intended release. A contaminated baseline must **never** be shipped. If verification fails, fix the constituents and rebuild — do not filter the symptom out of `out/`. ## 5. Repository boundary (hard rule) This repo lives on **`git.cadline.hu`** — the customer's Gitea, a different server from the operator's. Therefore: - **Never reference the operator's infrastructure here.** No operator issue URLs, no operator hostnames, no `Addresses-Issue:` trailers pointing at operator trackers — not in files, not in commit messages, not in branch names. - Issue tracking for this work lives on the operator side and is **not** linked from here. Describe *what* and *why* in the commit message itself instead. - An `@illusion.hu` e-mail address in a commit trailer (authorship attribution) is the one accepted exception. ## 6. Commits and branches - Conventional Commits, **English**, subject ≤72 chars, imperative mood. - Trailer: `Assisted-by: claude-code@` on AI-assisted commits. - **No** `Addresses-Issue:` / `Closes` (see §5). - Never force-push a branch someone else may be building on; on your own fresh branch it is fine (use `--force-with-lease`). - **Never push to `main`.** Open a pull request; a human merges. - **Never commit a secret.** These trees legitimately contain third-party code with embedded credentials — if you must refer to one, cite `file:line` and the *type*, never the value. Redaction applies to commit messages, docs and any output you produce. ## 7. Layer semantics (what goes where) | Directory | Holds | Source of truth | |---|---|---| | `core/` | CMS official package, deployed form, installer removed | upstream vendor | | `packages//` | extensions with obtainable upstream; modified ones authored as two commits (pristine → delta) so `git diff` documents our change | upstream + our patch | | `deployed//` | extensions with **no** obtainable upstream: vetted live files | live (vetted) | | `cadline//` | the site's own code | developer source repo | | `overrides/` | the site's modifications to core files, **on the current core's base** | developer source | | `malware-iocs.paths` | paths excluded from the build (droppers, leftovers) | this repo | `overrides/` rsyncs **last**, so it wins over `core/`. **Do not take a customized core file from live.** The updater skips customized files, so the live copy silently stays on an old base (the eu site ran a 3.9.2 version string over pre-3.8.13 `com_users` code). Take vanilla for the version you target, then apply only the genuine delta. ## 8. Definition of done A baseline branch is done when: - `build-baseline.sh` runs unmodified and produces `out/`; - all five checks in §4 pass, and the numbers are recorded in the commit message; - every layer's provenance is traceable (which file came from upstream, from the developer source, or vetted from live); - no secret value appears anywhere in the diff or the message.