Commit Graph
6 Commits
Author SHA1 Message Date
janniclas.loosenandClaude Opus 5.5 b24eb6c3e9 fix(security): no direct HTTP access to plugin PHP files (audit L2, cheap part)
Every file under src/ exits outside WordPress (defined('ABSPATH') ||
exit; after the use block, so a direct request no longer prints a fatal
error with the server path); divi-convert-fixture.php gets the WP_CLI
guard the other integration scripts have. A unit test keeps both true;
PSR1 side-effects warning excluded for the guard.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 19:32:12 +02:00
janniclas.loosenandClaude Opus 5.5 3eeb7eee90 refactor: split TextReplacer into HtmlTokens and StoredForms (M1)
- HtmlTokens (static, with the S45 docblock): SPLIT, RAW_OPEN, tokenize(),
  structure(), closes(), unterminated() moved unchanged; new RAW_ELEMENTS list.
- StoredForms: of() (was TextReplacer::forms), mentions(), unicodeEscaped() –
  the DB prefilter forms; TextReplacer::mentions() delegates, the ability's
  candidate query uses StoredForms::of().
- TextReplacer keeps problems(), apply() and the splice/safe() verification;
  safe() still runs the tokenizer and HtmlContext as two independent scans, in
  the same order.
HtmlTokensTest asserts RAW_ELEMENTS === HtmlContext::RAW and that RAW_OPEN opens
exactly those elements. TextReplacerTest and HtmlContextTest unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 18:14:49 +02:00
janniclas.loosenandClaude Opus 5.5 62ce99e558 refactor: ValueCleaner is the named choke point (H1)
The choke point moves out of AttrWriter into ValueCleaner (catalog, the
composition's VariableRef, sanitizer): clean() (was AttrWriter::cleanValue),
kindOf(), guardPath() and the private cleanNode/kind/unknownKind/tightens with
VALUE_KEY, URL_KEY, URL_SEGMENT, STRICTNESS, BUILDER_ONLY (its docblock now sits
on it; STRUCTURE stays in AttrWriter). Code of the pipeline moved unchanged.
- ValueCleaner's docblock is the one canonical description: steps 0-8 with their
  rulings in code order, invariants I1-I3; the step comments in cleanNode carry
  the same numbers. AttrWriter/ShortformWriter/Composer/TextReplacer docblocks
  point there and keep only their own rules.
- ValueCleaner::textReplacementFor($module) holds the D10 rule that was a
  closure in DiviReplaceTextAbility::replace (text/html only, clean() by kind).
- AttrWriter, ShortformWriter and Composer take the ValueCleaner; DiviComposition
  builds one per composition (cleaner()).
Tests: the eight AttrWriterTest methods that only exercised cleanValue/kindOf
moved verbatim to ValueCleanerTest (clean/kindOf, ComposeFixture::cleaner());
mixed tests call the cleaner in place; +1 test for textReplacementFor. Every
provider row and per-test assertion count unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 18:11:28 +02:00
janniclas.loosenandClaude Opus 5.5 874a92c6be refactor: split VariableRef into ControlChars and DynamicContentRules (H2)
- ControlChars::strip()/stripDeep() (was VariableRef::withoutControls/Deep) with
  the S32/S32a/S35 docblock; callers AttrWriter, ShortformWriter, TextReplacer and
  the setting cleaner use it.
- DynamicContentRules (design system + dynamic content map): fieldsFor() (was
  contentFields: meta prefix -> map -> loop_ -> design names -> unknown),
  cleanSetting() (was setting(): raw strip -> raw marker -> field type ->
  final-string check -> meta key check, same order), one private method per
  field family (textSetting, selectPostSetting, scalarSetting), isMetaKey(),
  checkMap(), and the constants DEFAULT_DYNAMIC_CONTENT, META_SETTINGS,
  DESIGN_SETTINGS, META_KEY.
- VariableRef keeps the syntax: const END = ')$' replaces the six literals,
  payloadOf() is shared by typeOf() and canonical(); unknownName() is public for
  the rules. Error messages unchanged.
Tests unchanged apart from a class name in DynamicContentMapTest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 18:05:38 +02:00
janniclas.loosenandClaude Opus 5.5 93d2bde286 fix: Task 8 review round 1 - quote-aware tokenizer, context check, skipped global nodes (S45)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 16:32:00 +02:00
janniclas.loosenandClaude Opus 5.5 d67cd0ede6 feat: lit/divi-replace-text with dry run and per-page tree_rev targets
TextReplacer replaces only in plain-text segments (S8 split), outside
script/style bodies, inserts the replacement cleaned per field kind and
refuses any result whose tags, entities or references differ from the
original plus the inserted text. Unchanged strings are never cleaned.
Per page all or nothing with the tree_rev lock; Throwables are reported
per page (S10).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 16:16:01 +02:00