Conversation
Tests and CLI docs used two real domains as examples/fixtures: - tissolution.it -> my.example.com (own domain) - rsgarage.eu -> example.net (info@ mailbox in mailbox_progress_test) Swap both for RFC 2606 reserved names so no real domain ships in the repo. Same-length substitutions keep struct/doc column alignment intact; behavior is unchanged (validation/scope/label tests assert on structure, not on the specific domain).
… zeroing Some cPanel builds report a fractional value for disk fields (e.g. Ftp::list_ftp_with_disk diskused = "57632.08" or a bare 13558.40). The old flexInt64 decoder only tried ParseInt, so any fractional value silently collapsed to 0, zeroing out every disk figure. Add a ParseFloat fallback that truncates to the integer part; null/empty/non-numeric still yield 0 without failing the surrounding decode (the field is informational only). Adds TestFlexInt64AcceptsFloats.
…yout Rebuilding a destination account under the SAME main domain legitimately puts the docroot at ~/public_html, which the classifier and the webfiles guard previously refused unconditionally (fail-closed). Add a narrow, defense-in-depth carve-out: - migrate: sameNameMainToMain relaxes the block ONLY when src and dest are both the MAIN domain, the canonical FQDN matches, and there is a single main_domain docroot. - webfiles: WebPlanItem.AllowDestPublicHTMLRoot (threaded through CanonicalDestDocroot / destDocrootEnv / Transfer and the guard script's ALLOW_PUBLIC_HTML_ROOT branch) relaxes ONLY the exact public_html-root equality refusal. Every other containment/escape/absolute/.. check stays fail-closed, and backupDest still refuses to back up the root even under the flag. Adds mainroot_test.go regression coverage.
…e cpsrvd decode
cpsrvd form-url-decodes uapi CLI argument values ('+' -> space, '%XX' ->
byte). The tool sends the MySQL password raw to Mysql::create_user /
set_password, so any generated or reused DB password containing '+' or '%'
was stored DECODED while wp-config and MYSQL_PWD kept the raw value,
silently breaking the migrated site's DB auth (the API still returns
success). The generated-password alphabet includes both '+' and '%', so
this hit a large fraction of runs.
encodeUAPIArgValue pre-encodes '%'->%25 and '+'->%2B in uapiArgsScript, so
after cpsrvd decodes, the stored value equals the original. Values with
neither byte are unchanged (no regression for existing calls).
Verified live on a sacrificial cPanel account (Mysql::create_user): a raw
'+'/'%2B' password authenticates only via its decoded form, confirming the
decode; the pre-encoded form round-trips exactly.
Add internal/events (stdlib-only): an optional Emitter on migrate.Options
drives a JSONL event stream (per-phase started/completed/failed across the
full run) plus an end-of-run report.json, behind four new flags: --run-id,
--output-dir, --json-events, --report-json. Zero-value Emitter = off; on the
CLI path the collector is always wired. Event Data payloads pass through a
key-based secret-redaction net before serialization; HostRef is {IP,User}.
Wired into runner.go (Options fields + emitEvent helper + run/connect/analyze
/compare emissions) and apply.go/apply_mailboxes.go (apply-phase emissions).
main.go grafts the flags onto dev's flat-flag flow; a broader namespace
rewrite and its --account-inventory are NOT included.
Built via a 3-constructor + judge panel; validated live against real cPanel
servers (dry-run: 18 events + report.json; apply --db: apply-phase events,
migrate_db/verify_db, redaction-clean). No new go.mod deps.
Replace every em-dash (U+2014) inside Go string literals with a context-appropriate colon (status/label prefixes) or comma (sentence continuations), so no em-dash reaches an operator via stdout, logs, --help, or the events.jsonl/report.json output. 332 replacements across 55 files; test goldens and *.golden fixtures updated in lockstep with their producers. No en-dashes existed. Pure text change with no behavior change (the character was never a runtime delimiter); build and the full test suite are green. Deferred as an optional follow-up: about 875 em-dashes in code comments across 169 files, non-operator-visible, left untouched for now. Two string-literal em-dashes inside embedded bash comments in remote-script raw strings are excluded (not operator-visible).
Add read-only cPanel inventory collectors (library, no CLI caller yet): FTP, SSL, PHP-per-vhost, MIME redirects, email accounts/forwarders/ autoresponders/filters/routing/default-address, DNS zones, and cron (read via `crontab -l` with command redaction). Each calls only List*/ Get*/Fetch* over UAPI or the new API2 (cpapi2) transport; no write path is reachable. api.go: refactor RunUAPI onto a shared runUAPIExec body and add RunUAPIRaw plus the API2 transport (api2ArgsScript/RunAPI2/parseAPI2). The already committed encodeUAPIArgValue stays applied to every UAPI value. types.go gains flexStringList (SSL SANs) and the generic api2Envelope. The DNS/email/cron WRITERS are deliberately left behind (separate unit). Customer PII is excluded: the realserver fixtures and their tests were dropped, only synthetic fixtures are used. No new go.mod deps.
…redact Key-based secret redaction was copied three times with drifting key lists: internal/events/redact.go, internal/cpanel/debug.go, and the cron command redactor in internal/cpanel/cron.go. The drift caused a real leak: a live PrestaShop cron job authenticated with secure=<token>, which slipped through because the cron list covered "secret" but not "secure". Add internal/redact (stdlib-only leaf: IsSensitiveKey, RedactMap, RedactJSON, Placeholder, Fragments) with one SUPERSET fragment list (token, secret, pass, pwd, key, auth, cred, cookie, session, bearer, secure) = the union of all three former lists, so every caller now redacts at least as much as any single copy ever did. The canonical list is unexported; Fragments() returns a copy for cron to build its command-line regex, so no caller can mutate and weaken it. events, debug.go and cron.go delegate to the shared package; cron sources its fragments from Fragments() so it cannot drift again. A completeness test fails if any historical fragment is dropped. No behavior weakened; no new go.mod deps.
Add internal/accountinventory (net-new): the offline brain over the read-only cpanel collectors. Collect drives every collector into a NormalizedInventory; the engines (diff, policy, coverage, dns/email/cron plan, checklist, acceptance merge) are stdlib-only and deterministic; the *_write.go files are LOCAL artifact serializers (JSON/Markdown), not server writes. This makes the previously-dead collectors live. CLI: a minimal "inventory" subcommand dispatch (diff|policy|dns-plan|email-plan |cron-plan|checklist, all offline) plus --account-inventory (collect both sides, write inventory_source.json / inventory_destination.json / inventory_report.md), hand-merged into dev's flat-flag main.go without any write namespaces. Excluded (Batch D): every server writer/verifier (dns/cron/email apply + verify). Consolidations: accountinventory.DNSRecordEntry collapsed onto the already imported cpanel.DNSRecord; normalized types clashing with cpanel names renamed to Norm* (NormForwarderEntry/NormAutoresponderEntry/NormEmailFilterEntry). Cron redaction is inherited from internal/redact via the cpanel collector. Secret hygiene: the raw cron command/env fields (command_clear/raw_line/ value_clear) are json:"-" and are never populated or serialized in this read-only path, so the inventory/diff/cron-plan artifacts carry only redacted commands plus SHA256 hashes (a cron authenticating with secure=<token> cannot leak the token). Job and env identity ride SHA256, keeping create/skip/manual classification unchanged. The raw fields remain defined for the future Batch-D apply writer, which re-collects the source crontab at apply time; a regression test plants a secret cron and asserts the artifact carries only the redacted form. Collect is implemented faithfully; the N+1 SSH batching optimization is a deferred follow-up. All real customer PII is scrubbed to synthetic / RFC5737. No new go.mod deps.
…ver-side execs The read-only account-inventory Collect issued ~42 sequential SSH round-trips per side (an N+1 over per-domain forwarders/autoresponders/DNS and per-mailbox filters). Fold it into 3 constant "bash -s" batched execs per side, reusing the proven sshx.StreamNul framed-output primitive that internal/migrate/collect.go already uses: pass 1 runs the no-arg calls + crontab, pass 2 runs the per-domain loops server-side, pass 2b runs the detail lookups (autoresponder bodies, get_filter). Round-trips go from ~13+2N+A+Z+M+F to a constant 3 per side. New internal/accountinventory/batch package: a prefetch/replay shim that captures every uapi/cpapi2/crontab response server-side (tag + NUL-framed, JSON verbatim) and replays them through the UNCHANGED cpanel.parseUAPI/parseAPI2 decoders and the UNCHANGED Collect assembly, so the NormalizedInventory stays byte-identical. No Go-side concurrency (one session at a time; no MaxSessions pressure, no race). cpanel gains only an exported EncodeUAPIArgValue passthrough; sshx.StreamNul is reused. The Batch-B cron redaction holds: raw crontab transits the framed stream but is redacted in Go before the artifact (command_clear/raw_line stay json:"-"). Verified: an in-process sequential-vs-batched equivalence test asserts byte-identical output, and a live A/B against a real cPanel account confirms the two inventories are byte-identical (apart from the collection timestamp and a pre-existing, server non-deterministic SSL-parse warning that varies run-to-run on the old binary too). Measured ~2.2x wall-clock on that account (28.5s to 13.1s). No new go.mod deps.
Real cPanel builds return the SSL cert numeric fields (not_before, not_after, is_self_signed, modulus_length) as JSON STRINGS, not numbers. SSLCertEntry declared them int64/int, so parseUAPI rejected the whole list_certs response on a real host and the account-inventory SSL section came back empty with a "cannot unmarshal string into ... of type int64" warning. The test fixture used integers, hiding it; a live run surfaced it. Retype the four fields to flexInt64 (which already backs Domains via flexStringList) so string and number both parse; cast at the two int64 consumers in the inventory SSL projection. Adds a string-numerics parse test. Live-verified: the SSL section now populates (25 certs) with no warning. No new go.mod deps.
… CLI Add internal/workbench (net-new leaf): a filesystem-backed migration-session model (14-state Status + forward-only Step transition matrix, non-secret SetupMeta/Endpoint, content-addressed artifact registry, provenance timeline, atomic 0600 writes + cross-process flock). Only non-stdlib import is internal/version. The safety_test firewall (no sshx/cpanel/config imports, no credential json tag, no write-verb) is preserved. CLI: a "migration" flat-flag dispatch (init|list|show|set-status|attach-artifact |archive) over a local store under CPANEL_MIGRATION_HOME, mirroring the inventory dispatch. Read/local-only, never dials a host, no server write. internal/webui and ui_cmd.go are NOT imported (Batch E). Security: AttachArtifact now derives the artifact dir from the validated store root + session id, never from the session.json artifact_dir field, so a crafted session.json cannot escape the store root. Live-verified: a session.json with artifact_dir="/tmp/ESCAPE" still writes inside root. Adds a containment regression test. flock.go gains a flock_windows.go stub so cross-platform builds compile. Timeline strings are in English; non-ASCII stripped. No new go.mod deps.
Bumps the minor-updates group with 1 update: [golang.org/x/crypto](https://github.com/golang/crypto). Updates `golang.org/x/crypto` from 0.53.0 to 0.54.0 - [Commits](golang/crypto@v0.53.0...v0.54.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.54.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the actions-updates group with 4 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action), [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) and [github/codeql-action/upload-sarif](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.36.3 to 4.37.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@54f647b...99df26d) Updates `github/codeql-action/analyze` from 4.36.3 to 4.37.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@54f647b...99df26d) Updates `golangci/golangci-lint-action` from db9de0fc1a667e1a49d2291a1a042dff081d78f6 to ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](golangci/golangci-lint-action@db9de0f...ba0d7d2) Updates `github/codeql-action/upload-sarif` from 4.36.3 to 4.37.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@54f647b...99df26d) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions-updates - dependency-name: github/codeql-action/analyze dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions-updates - dependency-name: golangci/golangci-lint-action dependency-version: ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a dependency-type: direct:production dependency-group: actions-updates - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Add the DNS apply/verify vertical to dev: - internal/cpanel/dns_apply.go: mass_edit_zone write primitives (MassEditZoneAdd/Remove/Batch), raw zone fetch, SOA serial extract and the stale serial detector. Runner-typed, transport agnostic. - internal/cpanel/dns_safety_test.go: AST allowlist guard confining the DNS write verbs to the two writer files, hardened with a wrapper call check the string scan alone lacks (the CamelCase MassEditZone* identifiers evade it). - internal/accountinventory: dnsapply/dnsverify report, backup and verify engines. Backup and verify retyped to cpanel.DNSRecord (dev keeps its cpanel.DNSRecord model; a separate DNSRecordEntry refactor is not adopted). - cmd/cpanel-self-migration: dns apply (+ --rollback) and dns verify orchestrators, plus a dns dispatch branch in main.go. Preview by default: dns apply writes nothing without --yes-apply-writes and dns verify is read only. - internal/sshx: a dev-native password-based DialDest (dest only), instead of an auth-based dialer. Safety preserved: preview-default gate, backup-or-nothing, SOA serial optimistic lock, replace preconditions, verify-after, apex as FQDN, 0600 artifacts, rollback refuses empty-old, verify stale-plan gate. Fixed a pre-existing verify-after bug that could resurrect an empty-records op to applied, with a regression test. Non-ASCII and long dashes scrubbed, test PII replaced with reserved example domains and RFC 5737 IPs. No new go.mod deps. build + vet + gofmt + full suite green.
Adds `cron apply` and `cron verify` on the destination account, mirroring the DNS write vertical (Batch D1). Both are preview by default: `cron apply` writes nothing without --yes-apply-writes and `cron verify` is read-only. Design: dev never persists cron clear-text (the inventory keeps only redacted commands for secret hygiene), so `cron apply` re-collects the source crontab fresh at apply time to obtain the clear installable command in memory. The offline plan supplies the reviewed decisions (which entries to create); the clear text is sourced live from the source host and never written to disk. The apply merges the create lines into the destination crontab and installs via the whole-crontab `crontab -` primitive. `cron verify` re-reads the destination and matches each op by its canonical redacted identity, so an entry installed in the clear still matches its redacted plan line and substring collisions cannot produce a false match. Safety contracts: - backup of the destination crontab written 0600 and hashed before the first write (backup or nothing); failure means nothing is written - whole-crontab merge, never a blind replace; `crontab -r` forbidden everywhere - a create whose source entry or schedule drifted since the plan is refused, never installed as a guess or with an unreviewed schedule - source re-collection is read-only; InstallCrontab runs only against the destination, funneled through a string+AST write guard and two allowlisted files - source clear-text is never persisted: the post-install crontab is not written into the report, and the install-failure output is redacted - exit codes: 0 ok or drift, 1 input/dial/write failure, 2 flags, 3 gated refusal (stale, refused precondition, fail-on-drift) Verify cannot value-verify a redacted secret; the report discloses that a clean verdict means the destination is consistent with the plan, not that every secret value matches.
…atch D2 Adds `email apply` and `email verify` on the destination account, mirroring the DNS (D1) and cron (D3) write verticals. Both preview by default: `email apply` writes nothing without --yes-apply-writes and `email verify` is read-only. Scope is credential-free by design: the vertical writes only email CONFIG (forwarders, default/catch-all address, autoresponders, filters, mail routing) via UAPI/API2. Mailbox creation and passwords stay in the existing migrate flow; no password verb (add_pop / passwd_pop / password_hash) appears anywhere in the imported code. Adaptation: the three raw inventory types were retyped to dev's pre-adapted Norm* names (NormForwarderEntry / NormAutoresponderEntry / NormEmailFilterEntry), qualifier-aware so cpanel's own read types are untouched; the duplicate deriveMDPath was dropped; the `email` dispatch branch was added to main.go. Safety contracts (verified): preview-default (fully offline), destination-only (the source is never dialed or written), backup-or-nothing 0600 before the first write, per-op freshness guard with a second pre-write re-check for the upsert verbs, never-overwrite of a human's autoresponder/filter (manual at plan time, refused at apply time), unconditional per-op verify-after, and rollback that inverts only the tool's own applied ops with the backup/report pairing enforced (sha256 + account + non-empty-sha-with-applied-ops + report format-version). Reports and backups are 0600; email content (bodies, filter rules) is persisted in clear as the rollback content-match basis (content, not credentials). Guard: a string+AST test funnels the eight email write verbs through the two allowlisted files, including a CamelCase-wrapper AST check (AddForwarder etc.) matching the DNS/cron guards. Autoresponder test bodies were translated from Italian to English (code is English-only); no real customer PII is imported (the real-domain realserver fixtures were intentionally left out of scope).
|
Too many files changed for review. ( Bypass the limit by tagging |
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
Important Review skippedToo many files! This PR contains 237 files, which is 187 over the limit of 50. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (237)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Dependency ReviewThe following issues were found:
License Issuesgo.mod
OpenSSF Scorecard
Scanned Files
|
There was a problem hiding this comment.
gosec found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Automated release PR for
v2.3.1.