feat: Support block previews without ModelsBuilder#274
feat: Support block previews without ModelsBuilder#274rickbutterfield wants to merge 1 commit intov5/devfrom
Conversation
Remove the requirement for strongly-typed ModelsBuilder models on disk. When no generated model exists, fall back to base block item types (BlockGridItem, BlockListItem, RichTextBlockItem) using IPublishedElement directly. Also adds blueprint resolution for Document Templates and a NoModelsTestSite example project. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR Review: feat: Support block previews without ModelsBuilderThis is a solid, well-motivated change. Removing the hard dependency on generated ModelsBuilder models significantly improves the package's accessibility. The overall approach is clean and the fallback logic is easy to follow. ✅ Positives
|
| { | ||
| markup = string.Format(Constants.ErrorMessages.WarningTemplate, Constants.ErrorMessages.ModelsBuilderError); | ||
| markup = string.Format(Constants.ErrorMessages.ErrorTemplate, string.Format(Constants.ErrorMessages.RenderError, ex.Message)); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, contentElementAlias)); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 29 days ago
In general, to fix log-forging issues, any user-controlled data included in log messages should be sanitized or encoded before being logged. For plain-text logs, the main concern is removing or normalizing newline and other control characters so that an attacker cannot inject apparent extra log lines or break the log format. For HTML-rendered logs, HTML encoding would be used instead.
In this case, the best minimal fix without changing existing functionality is to sanitize contentElementAlias right before it is used in the log message in the catch block of PreviewGridBlock. We can create a local sanitized variable derived from contentElementAlias which strips \r and \n (and optionally other line separators) using string.Replace, then pass that sanitized value into string.Format(Constants.ErrorMessages.LoggerError, ...). This keeps the meaning of the log entry intact while preventing newline-based log forging. The change is local to the PreviewGridBlock method in src/Umbraco.Community.BlockPreview/Controllers/BlockPreviewApiController.cs and does not require new imports or changes to other methods.
| @@ -170,7 +170,10 @@ | ||
| catch (Exception ex) | ||
| { | ||
| markup = string.Format(Constants.ErrorMessages.ErrorTemplate, string.Format(Constants.ErrorMessages.RenderError, ex.Message)); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, contentElementAlias)); | ||
| var safeContentElementAlias = (contentElementAlias ?? string.Empty) | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, safeContentElementAlias)); | ||
| } | ||
|
|
||
| string? cleanMarkup = CleanUpMarkup(markup); |
| { | ||
| markup = string.Format(Constants.ErrorMessages.WarningTemplate, Constants.ErrorMessages.ModelsBuilderError); | ||
| markup = string.Format(Constants.ErrorMessages.ErrorTemplate, string.Format(Constants.ErrorMessages.RenderError, ex.Message)); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, contentElementAlias)); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 29 days ago
In general, to fix log-forging issues you should sanitize or encode any user-supplied data before including it in log messages. For plain-text logs, the safest minimal approach is to strip newline and carriage-return characters (and optionally other control characters) from user input. This preserves most of the content while preventing attackers from injecting additional fake log lines.
For this specific case in src/Umbraco.Community.BlockPreview/Controllers/BlockPreviewApiController.cs, the best fix with minimal behavior change is to sanitize contentElementAlias before using it in the error log. We can introduce a local sanitized variable in the catch block that removes \r and \n from contentElementAlias and then use that sanitized value in string.Format. This avoids altering the behavior of other code paths that may legitimately require the raw contentElementAlias, and it requires no new dependencies. All changes stay within the shown method PreviewListBlock, and we only touch the lines around the logging call.
Concretely:
- In the
catch (Exception ex)block ofPreviewListBlock, add a new local stringsafeContentElementAliasthat is computed fromcontentElementAliasby replacing newline and carriage-return characters with empty strings. - Use
safeContentElementAliasinstead ofcontentElementAliaswhen callingstring.Formatfor the log message. - No additional imports or methods are required; we use
string.Replacewhich is inSystemand already available.
| @@ -223,7 +223,10 @@ | ||
| catch (Exception ex) | ||
| { | ||
| markup = string.Format(Constants.ErrorMessages.ErrorTemplate, string.Format(Constants.ErrorMessages.RenderError, ex.Message)); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, contentElementAlias)); | ||
| var safeContentElementAlias = (contentElementAlias ?? string.Empty) | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, safeContentElementAlias)); | ||
| } | ||
|
|
||
| string? cleanMarkup = CleanUpMarkup(markup); |
| { | ||
| markup = string.Format(Constants.ErrorMessages.WarningTemplate, Constants.ErrorMessages.ModelsBuilderError); | ||
| markup = string.Format(Constants.ErrorMessages.ErrorTemplate, string.Format(Constants.ErrorMessages.RenderError, ex.Message)); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, contentElementAlias)); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 29 days ago
To fix the issue, user-controlled data should be sanitized before being included in log messages. For plain text logs, the key defense is to strip or replace newline and other control characters so a user cannot break the log’s line structure. We should apply such normalization as close as possible to the logging call and only affect the logged representation, not the value used elsewhere, to avoid changing application behavior.
The best targeted fix here is to sanitize contentElementAlias only for logging inside the catch block of PreviewRichTextMarkup. We can create a local variable (e.g., safeContentElementAlias) that derives from contentElementAlias with problematic characters removed. For example, we can call Replace to remove \r and \n, and optionally any other special characters we consider risky, and then use that sanitized variable in the string.Format call. This confines the change to the logging behavior while keeping the rest of the method and controller unchanged.
Concretely, in src/Umbraco.Community.BlockPreview/Controllers/BlockPreviewApiController.cs, within the catch (Exception ex) block of PreviewRichTextMarkup, we should:
- Introduce a local
safeContentElementAliasthat is derived fromcontentElementAliaswithEnvironment.NewLine,\r, and\nremoved (or replaced with spaces). - Update the
_logger.LogErrorline to usesafeContentElementAliasinstead of the rawcontentElementAlias.
No new imports are needed, as we can use string.Replace and Environment.NewLine from the existing BCL.
| @@ -270,7 +270,11 @@ | ||
| catch (Exception ex) | ||
| { | ||
| markup = string.Format(Constants.ErrorMessages.ErrorTemplate, string.Format(Constants.ErrorMessages.RenderError, ex.Message)); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, contentElementAlias)); | ||
| var safeContentElementAlias = (contentElementAlias ?? string.Empty) | ||
| .Replace(Environment.NewLine, string.Empty) | ||
| .Replace("\r", string.Empty) | ||
| .Replace("\n", string.Empty); | ||
| _logger.LogError(ex, string.Format(Constants.ErrorMessages.LoggerError, safeContentElementAlias)); | ||
| } | ||
|
|
||
| string? cleanMarkup = CleanUpMarkup(markup); |
Summary
BlockGridItem,BlockListItem,RichTextBlockItem) usingIPublishedElementdirectly when no generated model existsCheckGeneratedModelsExist()gate andITypeFinderdependency from the API controller; replace withIContentServicefor blueprint resolutionNoModelsTestSiteexample project demonstrating usage without ModelsBuilderBlockModelFactoryTest plan
BlockModelFactoryTests— all tests should pass🤖 Generated with Claude Code