Skip to content

candy-shine: audit remediation steps 1-13#1164

Merged
detain merged 11 commits into
masterfrom
ai/candy-shine-fix
Jun 29, 2026
Merged

candy-shine: audit remediation steps 1-13#1164
detain merged 11 commits into
masterfrom
ai/candy-shine-fix

Conversation

@detain

@detain detain commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Summary

  • Step 1: Reject malformed ansi:/ansi256: color specs with regex + range validation; add theme.bad_color lang key
  • Step 2: Make BlockStack and StyleSheet final; add is_file() guard to Theme::fromJson()
  • Step 3: Add C0/ESC sanitization via stripControls(), $sanitize flag, $textIsPlain memoization; wire into all text rendering methods; add withSanitize()/sanitize() builders; add SanitizeTest.php
  • Step 4: OSC-8 URL sanitization via safeUrl() stripping C0/ESC/BEL unconditionally in resolveUrl()
  • Step 5: Wire autolink theme slot — bare URLs now use $theme->autolink ?? $theme->link
  • Step 6: Wire table-separator glyphs via buildTableBorder() using theme slots with rounded-corner fallbacks
  • Step 7: Wire definition-list rendering via DescriptionListExtension + renderNode cases for MdDescriptionList/DescriptionTerm/Description
  • Step 8: Remove dead conceal flag from Theme
  • Step 9: Fix broken {@see EmojiMap} doc-comment; fix stale doc-block placement at extractBlankRuns
  • Step 10: Remove dead availableWidth from BlockContext; memoize indent/margin in BlockStack; remove unused popTo() method
  • Step 11: Memoize textIsPlain at construction (merged with Step 3)
  • Step 12: Syntax highlighter already guarded via preg_match_all === false check
  • Step 13: Backfill tests for applyCase unknown case and withSanitize() builder

Test plan

  • 232 tests, 426 assertions (baseline was 214)

detain added 11 commits June 29, 2026 02:00
Mirrors charmbracelet/glamour parsing contract — malformed specs
(ansi:300, ansi256:abc, etc.) now throw InvalidArgumentException
instead of silently casting to 0 via blind (int) cast.

Add theme.bad_color lang key.
…son path

Add is_file() check in Theme::fromJson before reading — surfaces
the common not-a-file/missing case cleanly rather than relying on
the @-suppressed file_get_contents failure.
…moize textIsPlain

Add stripControls() that removes C0 bytes except tab/newline — this
closes the ANSI-injection vector for source-derived text.

Constructor gains $sanitize=true flag (default on) and computes
$textIsPlain once at construction (Step 11 memoization). Add
withSanitize(bool) builder and sanitize() short alias.

Apply sanitization to source literals in: renderText, renderCode
(inline), renderIndent (formerly IndentedCode in renderNode),
renderFencedCode (body before syntax highlighter), renderHtmlBlock,
renderHtmlSpan.

New SanitizeTest covers ESC-strip from text/inline-code/html-block/
fenced-code; sanitize=false passthrough; tab+newline preservation.
Add safeUrl() static method that strips C0/ESC/BEL from any URL
and apply it unconditionally at resolveUrl() — the single choke
point for all link/image hrefs and the visible (url) suffix.

New SanitizeTest cases: hyperlink-url ESC/BEL stripped,
image-url ESC/BEL stripped, normal URL unaffected.
Bare URL (autolink) now uses $theme->autolink style, falling back
to $theme->link when autolink is null.

New RendererTest cases: testAutolinkUsesAutolinkSlot (bold vs
underline styling), testAutolinkFallsBackToLinkWhenAutolinkNotSet.
Replace hard-coded Border::rounded() with buildTableBorder() that
constructs a Border using theme tableColumnSeparator/tableRowSeparator/
tableCenterSeparator glyphs while preserving rounded corners.
Register League\CommonMark\Extension\DescriptionList\DescriptionListExtension
in the constructor. Add renderNode cases for DescriptionList/DescriptionTerm/
Description and implement renderDescriptionList/renderDescriptionTerm/
renderDescription with proper theme slot fallbacks.

Note: DescriptionListExtension confirmed vendored in league/commonmark.
New RendererTest cases: testDefinitionListRenders (styled with bold/italic
slots), testDefinitionListWithNullStylesRenders (plain fallback).
conceal property was documented as a no-op placeholder with no
readers. Remove the constructor param and doc-comment from Theme.
Update RendererRound2Test::cloneWithOverrides slot list.
…rdering

Replace {see EmojiMap} with 'built-in shortcode map' in withEmoji
doc-comment since EmojiMap class does not exist.

Move the dangling @return list<int> doc-comment (was above expandEmojiShortcodes)
to its rightful position directly above extractBlankRuns method.
…t/margin

Drop  from BlockContext entirely — it was passed in on every
block push but never read; availableWidth() is always computed on demand.

Memoize accumulatedIndent and marginCount in BlockStack itself via running
totals updated incrementally in push()/pop(). This changes O(n) traversal on
every width query to O(1).

Remove BlockStack::popTo() since it would corrupt the memoized counters —
no caller in the renderer ever used it. Remove the two test methods that
exercised it.
Add testApplyCaseUnknownFallsBackToIdentity: verifies that an unknown
headingCase value in Theme falls through to identity (text unchanged).

Add testWithSanitizeReturnsNewInstance: verifies that withSanitize(true)
and withSanitize(false) each return a new distinct Renderer instance.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@codacy-production

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 critical · 2 medium · 4 minor

Alerts:
⚠ 7 issues (≤ 0 issues of at least minor severity)

Results:
7 new issues

Category Results
UnusedCode 2 medium
Security 1 critical
CodeStyle 4 minor

View in Codacy

🟢 Metrics 5 complexity · 0 duplication

Metric Results
Complexity 5
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@detain detain merged commit 6f99c47 into master Jun 29, 2026
17 of 18 checks passed
@detain detain deleted the ai/candy-shine-fix branch June 29, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant