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
128 lines
6.2 KiB
Markdown
128 lines
6.2 KiB
Markdown
# 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.
|