www_archline_hu/AGENTS.md
imre.agent e60b81f085 docs: document the repo as a multi-site baseline generator
The repo now carries more than one site: main holds www.archline.hu and
site/archlinexp.eu holds the eu site, both driven by the same
site-agnostic generator. README.md still described a single-site repo.

Rewrite README.md around the actual model: one branch per site, the
baseline's dual role (known-good reference for the HIDS diff AND the
deployment source of truth), the verify-before-you-trust rule, and why a
customized core file must never be taken from live (the updater skips
customized files, so live silently keeps a stale base -- the eu site ran
a 3.9.2 version string over pre-3.8.13 com_users code).

Add AGENTS.md for automated agents: the cardinal never-build-from-an-
infected-tree rule, the mandatory post-build checklist (YARA, IOC paths,
disguise sweep, the structural core-namespace rule that caught what YARA
missed, core version assertion), the repository boundary, commit rules
and layer semantics.

Assisted-by: claude-code@claude-opus-4-8
2026-07-17 12:52:40 +02:00

6.2 KiB

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/<tpl>/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@<model> 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/<slug>/ extensions with obtainable upstream; modified ones authored as two commits (pristine → delta) so git diff documents our change upstream + our patch
deployed/<slug>/ extensions with no obtainable upstream: vetted live files live (vetted)
cadline/<slug>/ 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.