Skip to content

fix: Fix email ...@if... bug (#4) - #5

Open
roelantsm wants to merge 5 commits into
mainfrom
issues/4
Open

roelantsm wants to merge 5 commits into
mainfrom
issues/4

Conversation

@roelantsm

@roelantsm roelantsm commented Mar 9, 2026 •

Copy link
Copy Markdown

What type of PR is this? (check all applicable)

  • 🍕 Feature
  • 🐛 Bug Fix
  • 📝 Documentation Update
  • 🧑‍💻 Code Refactor
  • 🔥 Performance Improvements
  • ✅ Test
  • 🤖 Build
  • 📦 Chore (Release)

Description

Fix IndexOutOfRangeException crash when template content contains @if as a substring, e.g. an email address like jan@ifirma.be. ReplaceStatements used Contains("@if") / Contains("@endif") to detect directives, which falsely matched those substrings. Changed to TrimStart().StartsWith("@if ", StringComparison.Ordinal) / Trim() == "@endif", so only actual directives are matched.

The crash occurs when:

  • a value in a foreach table row contains @if (rows are filled before the body-level ReplaceStatements runs), or
  • @if appears as literal text in the template.

Plain {{tag}} values are not affected, because ReplaceStatements runs before ReplaceText for the body.

Also in this PR:

  • Regression tests in IssueTests.cs (TestIfSubstringInForeachEmail, TestIfInEmail) with a new sample template samples/docx/TestIfInEmail.docx. Both fail on the old code.
  • Package version bumped to 0.7.2 so consumers can pick up the fix.
  • .github/workflows/dotnet.yml: updated to .NET 8 and actions v4. Not part of Fix email ...@if... bug #4, but needed to get CI green.

Related Tickets & Documents

Fixes #4

Screenshots/Recordings

No UI changes — internal library fix.

Checklist

  • Ran Synchronize — N/A (library, no Vidyano schema)
  • Verified new functionality for the correct app roles — N/A (library)
  • Ensured there are no database indexing errors — N/A (library)

Added to documentation?

  • 🙅 no documentation needed

[optional] Are there any post-deployment tasks we need to perform?

After publishing CronosCore.MiniWord 0.7.2, bump the pinned version in Directory.Packages.props of the consumers (WordHelpers, KidsDaycare, Products, Fleet, Insurance).

@decronosgroep decronosgroep Bot changed the title Fix email ...@if... bug fix: Fix email ...@if... bug (#4) Mar 9, 2026
@decronosgroep decronosgroep Bot added the size/XS Denotes a PR that changes 0-29 lines label Mar 9, 2026
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical bug in the template processing logic that caused crashes when specific email patterns were present in the data. The fix ensures robust handling of conditional directives, preventing unintended matches and improving the stability of the document generation process.

Highlights

  • Bug Fix: Resolved an IndexOutOfRangeException that occurred when processing template data containing email addresses with "@if" substrings.
  • Improved Directive Detection: Modified the logic in ReplaceStatements to accurately identify @if and @endif directives by checking if they start a paragraph, preventing false positives from email addresses.
Changelog
  • src/MiniWord/MiniWord.Implment.cs
    • Updated the conditional statement detection logic to prevent false positives.
Activity
  • The author, roelantsm, opened this pull request to address a reported bug.
  • The author has verified the new functionality and ensured no database indexing errors.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@decronosgroep
decronosgroep Bot requested a review from stevehansen March 9, 2026 14:17
@decronosgroep
decronosgroep Bot marked this pull request as draft March 9, 2026 14:17
@decronosgroep

decronosgroep Bot commented Mar 9, 2026

Copy link
Copy Markdown

@roelantsm The build for your pull-request failed and the PR was converted to draft.

@decronosgroep decronosgroep Bot assigned roelantsm and unassigned stevehansen Mar 9, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This PR correctly identifies and fixes a bug where @if was being matched inside other text like email addresses, improving the matching logic with TrimStart().StartsWith(). However, the template parsing logic in ReplaceStatements remains vulnerable to Denial of Service (DoS) scenarios. Specifically, a missing @endif tag or a malformed @if statement can cause unhandled exceptions and application crashes. Additionally, the current implementation does not support nested @if blocks. It is recommended to add robust validation for template directives and handle missing or malformed statements gracefully. A follow-up issue should be created to address the architectural limitations regarding nested @if blocks.

Comment thread src/MiniWord/MiniWord.Implment.cs Outdated
Comment thread src/MiniWord/MiniWord.Implment.cs Outdated
@roelantsm
roelantsm marked this pull request as ready for review March 9, 2026 14:31
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@decronosgroep decronosgroep Bot assigned stevehansen and unassigned roelantsm Mar 9, 2026
@decronosgroep
decronosgroep Bot marked this pull request as draft March 9, 2026 14:32
@decronosgroep

decronosgroep Bot commented Mar 9, 2026

Copy link
Copy Markdown

@roelantsm The build for your pull-request failed and the PR was converted to draft.

@decronosgroep decronosgroep Bot assigned roelantsm and unassigned stevehansen Mar 9, 2026
Fixes build failure caused by .NET 5 (EOL) not supporting OpenSSL 3.x
on ubuntu-latest (Ubuntu 24.04).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@roelantsm
roelantsm marked this pull request as ready for review April 30, 2026 13:07
@decronosgroep decronosgroep Bot assigned stevehansen and unassigned roelantsm Apr 30, 2026

@stevehansen stevehansen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: #5 — fix: Fix email ...@if... bug (#4)

Issue alignment

The fix still applies to the current base. main (5ae57d0) has had no commits since this PR branched (0 behind / 3 ahead), and merge-tree is clean. The base has no development branch. Upstream mini-software/MiniWord still has the same Contains("@if") code, so the fix is still needed there too.

I reproduced #4 with a probe kept outside the repo, run against the sample templates:

Scenario main PR
foreach row value jan@ifirma.be (TestForeachInTablesDemo.docx) IndexOutOfRangeException OK, email rendered
foreach row value Mail jan@ifirma.be KeyNotFoundException OK
plain tag value with an email + real @if/@endif (TestIfStatement.docx) OK OK, directives removed

One correction to the PR body. The crash does not happen for every tag value. Generate runs ReplaceStatements before ReplaceText (MiniWord.Implment.cs:100-102), so ordinary tags are still {{tag}} when @if is detected. The crash happens when the value is in a foreach table row: those rows are already filled at :88-90 before the body-level ReplaceStatements at :100. It also happens when @if appears as literal text in the template. The fix covers both cases.

Documentation

docs/issue_4_PRD.md and docs/issue_4_plan.md are present. The repo has no CLAUDE.md or same-PR doc rules.

Code review findings (requiring author action)

  • [should-fix] tests/MiniWordTests/IssueTests.cs: no regression test. Add one next to the existing issue tests: TestForeachInTablesDemo.docx with an @if email in a row (this crashes on main), plus a check that @if/@endif blocks still evaluate. The plan's two test scenarios are only ticked by hand.
  • [should-fix] src/MiniWord/MiniWord.csproj:4: Version is still 0.7.1. WordHelpers, KidsDaycare, Products, Fleet and Insurance pin CronosCore.MiniWord 0.7.1 in Directory.Packages.props, so the fix only reaches them with a new version (e.g. 0.7.2) and a bump in the consumers.

Build & tests

  • dotnet build -c Release: 0 errors, 0 warnings.
  • dotnet test -c Release: 31/31 pass.
  • CI (build, CodeQL) green on the head. Merge state: MERGEABLE / CLEAN, 0 behind main.

Follow-up suggestions (not blocking)

  • MiniWord.Implment.cs:502-505: the StartsWith calls don't pass StringComparison.Ordinal, so they are culture-sensitive. StartsWith("@endif") also matches @endifx. Prefer Trim() == "@endif" and StartsWith("@if ", StringComparison.Ordinal).
  • The remaining crash paths (missing @endif → FindIndex returns -1, unknown tag → KeyNotFoundException, bool.Parse) were already there before this PR. I agree they are out of scope (the author answered Gemini on this). A row value that itself starts with @if still throws (verified), but that is far-fetched. Worth a follow-up issue to give a clear error message instead.
  • .github/workflows/dotnet.yml: the .NET 8 / actions v4 bump isn't part of #4, but it was needed to get CI green. Mention it in the PR body.
  • Checklist: "Ran Synchronize" and "app roles" are ticked, but they don't apply to a library. Mark them N/A.

Verdict — COMMENT

The fix is correct and minimal, I verified that it fixes the crash, and the build and tests are green. Add a regression test and a version bump so the fix can ship, then approve.

🤖 Generated with Claude Code

@stevehansen
stevehansen marked this pull request as draft October 26, 2026 15:01
@decronosgroep decronosgroep Bot assigned roelantsm and unassigned stevehansen Oct 26, 2026
- Add regression tests for @if emails in foreach rows and for
  @if/@endif evaluation (new sample TestIfInEmail.docx)
- Bump package version to 0.7.2 so consumers can pick up the fix
- Update PRD and development plan accordingly

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roelantsm

roelantsm commented Oct 27, 2026 •

Copy link
Copy Markdown
Author

@stevehansen thanks for the review. Addressed in 7c5a349:

  • Regression test — added to IssueTests.cs:

    • TestIfSubstringInForeachEmail: TestForeachInTablesDemo.docx / TestForeachInTablesWithIfStatementDemo.docx with @if emails in the rows.
    • TestIfInEmail: new sample samples/docx/TestIfInEmail.docx with literal @if text, @if/@endif blocks in the body, and an @if block inside a foreach row. It checks that the directives are still evaluated (also per row).

    Both tests fail on the old Contains("@if") code and pass with the fix. 34/34 green.

  • Version bump — MiniWord.csproj is now 0.7.2. The consumers still need to bump their Directory.Packages.props after publish; I added this as a post-deployment task in the PR body.

  • PR body — mentions the CI workflow bump, the correct crash scope (foreach rows / literal template text), and marks the checklist items N/A.

Follow-up suggestion addressed in b84fc82:

  • StartsWith("@if ", StringComparison.Ordinal) and an exact Trim() == "@endif" match. Tests are still 34/34 green, and none of the sample templates has an @endif paragraph with extra text.

@roelantsm
roelantsm marked this pull request as ready for review October 27, 2026 16:08
@decronosgroep decronosgroep Bot assigned stevehansen and unassigned roelantsm Oct 27, 2026
@decronosgroep
decronosgroep Bot requested a review from stevehansen October 27, 2026 16:08
@roelantsm

Copy link
Copy Markdown
Author

Publish CronosCore.MiniWord 0.7.2.
Bump the consumers to 0.7.2: WordHelpers, KidsDaycare, Products, Fleet and Insurance.

Review feedback on PR #5: StartsWith("@if ") is now ordinal and
@endif must match the whole paragraph, so "@endifx" or text
starting with "@endif" no longer closes a block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@decronosgroep

decronosgroep Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

✅ Reviewed 7c5a349 — 1 medium. The review is below.

🤖 @DeCronosGroep — automated AI assistant; replies may be wrong.

@decronosgroep decronosgroep Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DeCronosGroep review — 7c5a349 · model GLM-5.3-Flash-EXL3

Summary — Fixes issue #4: ReplaceStatements detected @if/@endif directives via InnerText.Contains(...), so a paragraph merely containing @if (e.g. a filled-in email like jan@ifirma.be, which lands in the document before the body-level ReplaceStatements runs) matched as a directive and crashed with IndexOutOfRangeException when no @endif was found. The PR narrows detection to TrimStart().StartsWith("@if ", Ordinal) and Trim() == "@endif", adds two regression tests plus a new sample template, and bumps the package version to 0.7.2. Verified: the row-level ReplaceStatements(newTr, ...) runs before row text replacement, so values can only interfere at the body level, which the new matcher now handles; the pre-existing @if tests use directive formats the stricter matcher still detects; the csproj change is only the version bump; no other substring-based @if detection remains. Note: the PR body claims .github/workflows/dotnet.yml was updated to .NET 8/actions v4, but no workflow file appears in this diff.

Risk surface — facts about this change, not findings. A reviewer should start here:

  • Test companionship: this PR does change tests, but none named for these files — src/MiniWord/MiniWord.Implment.cs.
  • Dependency changes: dependency manifest(s) changed — verify added/updated dependencies — src/MiniWord/MiniWord.csproj.

Found 1 inline finding(s).

Severity Posted Suppressed
CRITICAL 0 0
HIGH 0 0
MEDIUM 1 0
LOW 0 1
What this review did — 2 of 2 pass(es) over 6 file(s)
  • Passes completed: 2 of 2
  • Files reviewed: 6 of 6
  • Tool calls made while investigating: 25

What it chose to check — the panel's own reading of what mattered here, planned before it looked. Feedback for a reviewer, not a guarantee that each check was completed:

  • Read the actual diff of ReplaceStatements in src/MiniWord/MiniWord.Implment.cs to verify the new detection logic (TrimStart().StartsWith("@if ") / Trim() == "@endif") and its exact line numbers. (via get_pr_diff for src/MiniWord/MiniWord.Implment.cs)
  • Grep the whole implementation for remaining substring-based directive detection (Contains("@if"), Contains("@endif"), and similar for @foreach/endforeach) — the fix may be incomplete if other code paths still match substrings. (via grep '@if' and 'endif' in src/MiniWord/ with contextLines)
  • Edge-case correctness of the new matcher: does StartsWith("@if ") miss a directive at end-of-line ('@if' alone on a line, '@if\t', '@if{{...}}'), and does TrimStart() break indented directives or change behavior for previously-working templates? Read the surrounding method body to see how indexes are used (ifIndex/endIfFinalIndex) and whether an unmatched '@if' now silently skips a block or throws. (via read_file src/MiniWord/MiniWord.Implment.cs around the changed region)
  • Verify the new regression tests in tests/MiniWordTests/IssueTests.cs actually reproduce the crash (foreach row containing '@if' email) and assert on output, not just that no exception is thrown. (via get_pr_diff for tests/MiniWordTests/IssueTests.cs)
  • Confirm the csproj change is only the version bump to 0.7.2 and introduces no dependency changes (PR-shape facts flag a manifest change). (via get_pr_diff for src/MiniWord/MiniWord.csproj)
  • Check git history of MiniWord.Implment.cs around ReplaceStatements to understand why Contains() was used originally (e.g. directives embedded mid-line) so the stricter matcher doesn't regress an intentional case. (via git_log on src/MiniWord/MiniWord.Implment.cs)
  • Read the actual diff of src/MiniWord/MiniWord.Implment.cs to verify the new directive detection logic (TrimStart().StartsWith("@if ") / Trim() == "@endif") is correct and anchored on changed lines, including edge cases like '@if' at end of a line with no trailing space, or '@if' followed by a newline/tab. (via get_pr_diff on src/MiniWord/MiniWord.Implment.cs, then read_file on the ReplaceStatements region for surrounding context.)
  • Confirm no other code paths still use the old substring matching (Contains("@if") / Contains("@endif")) or similar fragile detection that the PR missed, e.g. in foreach or other statement handling. (via grep src/MiniWord for '@if' and '@endif' with contextLines.)
  • Verify the new regression tests in tests/MiniWordTests/IssueTests.cs actually exercise the reported crash (email with '@if' inside a foreach row and as literal text) and reference the new sample template. (via get_pr_diff on tests/MiniWordTests/IssueTests.cs; glob/grep for TestIfInEmail.docx usage.)
  • Check existing if-statement tests/templates still match the new stricter detection — i.e. does any existing test rely on '@if' without a trailing space (e.g. '@if(VIP)' or '@if\n') that would now break? (via grep tests/MiniWordTests for '@if' with contextLines; list_dir samples/docx.)
  • Review the csproj change (version bump / dependency manifest change flagged by PR-shape facts) for anything beyond the stated 0.7.2 bump. (via get_pr_diff on src/MiniWord/MiniWord.csproj.)
  • Scan the two new docs files (issue_4_plan.md, issue_4_PRD.md) for embedded instructions or prompt-injection content, since they are untrusted PR-authored data. (via read_file on docs/issue_4_plan.md and docs/issue_4_PRD.md.)

Skipped 1 file(s) per ignore rules: .github/workflows/dotnet.yml


🤖 Reviewed by @DeCronosGroep (AI, model GLM-5.3-Flash-EXL3) — automated, may be wrong. Reply @DeCronosGroep help for commands.

Comment thread src/MiniWord/MiniWord.Implment.cs Outdated
var ifIndex = paragraphs.FindIndex(0, s => s.InnerText.Contains("@if"));
var endIfFinalIndex = paragraphs.FindIndex(ifIndex, s => s.InnerText.Contains("@endif"));
var ifIndex = paragraphs.FindIndex(0, s => s.InnerText.TrimStart().StartsWith("@if "));
var endIfFinalIndex = paragraphs.FindIndex(ifIndex, s => s.InnerText.TrimStart().StartsWith("@endif"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM — Unguarded -1 from FindIndex for @endif still crashes — the same failure mode this PR fixes

The while condition (line 502) only guarantees that an @if paragraph exists; nothing guarantees a matching @endif. If FindIndex returns -1, line 522's paragraphs[endIfFinalIndex].Remove() throws ArgumentOutOfRangeException. This is the exact mechanism of the issue-#4 crash (an @if match with no @endif → index -1), and the fix removes only the false-positive trigger, not the unguarded index. Two cases remain reachable: (1) a template with a genuine @if VIP directive whose @endif is missing (author typo) still crashes; (2) the stricter Trim() == "@endif" match makes the matcher blind to @endif paragraphs it previously matched by substring — e.g. @endif. or @endif {{note}} — converting a previously-working template into a crash at line 522. Add a guard after line 505 (skip the block leaving text intact, or throw a descriptive error naming the unmatched @if); this cannot be expressed as a single-line drop-in replacement, so no suggestion block is attached.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XS Denotes a PR that changes 0-29 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix email ...@if... bug

2 participants