Repository navigation
Description
Track progress on SonarCloud findings for this project.
Addressed in commits af11135 and 37ca7b9 (2026-06-21), and #679 (2026-11-08):
✅ Completed
Security
- S7637 — Pin
timheuer/base64-to-fileCI action to full commit SHA (release.yml) - S7636 — Move
NUGET_ORG_PUSH_KEYout of inlinerun:block intoenv:section - ReDoS hotspots — Add 1-second timeout to all 7
Regexinstances inWhitespaceRemover.cs
Reliability (real bugs)
- S2955 — Replace
== nullon unconstrained generic type parameters withEqualityComparer<T>.Default.Equals(value, default)inHtmlEncoder,HtmlEncoderLegacy,DictionarySlim,FixedSizeDictionary,GenericDictionaryAccessor,ReadOnlyGenericDictionaryAccessor
False positives (suppressed with justification)
- S7133 —
ReaderWriterLockSlimExtensions: lock IS released viaDisposableContainerinusing; not a bug - S2234 —
IteratorBinder: swappingtemplate/ifEmptyfor theInversecase is intentional - S1944 —
PartialBinder: cast is a C# explicit operator onExpressionContainer<T>, not a runtime hierarchy cast
Design / API quality
- S3881 — Seal
TextEncoderWrapperandClosureBuilder(IDisposable pool types with private constructors) - S3881 — Seal
ReusableStringWriter(IO/PolledStringWriter.cs); its only constructor is private, so sealing is not an API break.Dispose(bool)intentionally returns the instance to the pool rather than callingbase.Dispose(refactor: address pending SonarCloud S3881/S3776 items in lexer and ReusableStringWriter #679) - S3218 — Use explicit interface implementation for
IInternalObjectPoolPolicy<T>.Create()in all six pool policy types to eliminate method-name shadowing - S112 — Replace
IndexOutOfRangeExceptionwithArgumentOutOfRangeExceptioninArguments.csandSubstring.cs - S1168 — Return
Array.Empty<Expression>()instead ofnullinHandlebarsExpressionVisitor - S3993 — Add
[AttributeUsage(AttributeTargets.Class)]toFeatureOrderAttribute - S2743 — NOSONAR with justification on
ImmutableStack<T>pool field (intentional per-T instance) - S3246 — Add
outcovariance toValueTypeGetterDelegate<T, TValue> - S3011 — NOSONAR with justification on
ReflectionMemberAccessorprivate method lookup - S2589 — Remove always-false dead branch in
MissingBlockHelperDescriptor - S2933 — Make
_enumeratorfieldreadonlyinExtendedEnumerator - S1066 — Merge nested
ifstatements inDelegatedMemberAliasProvider - S3358 — Extract nested ternaries in
EndExpressionToken,StartExpressionToken,PartialBlockAccumulatorContext - S3928 — Add meaningful message to
ArgumentOutOfRangeExceptioninBlockHelperFunctionBinder - S927 — Rename parameter
exp→nodeinHandlebarsExpressionVisitor.Visit()to match base class signature
Maintainability
- S3776 — Extract
ProcessStatementfromWhitespaceRemover.ProcessTokens(cognitive complexity reduced; partial indentation logic preserved) - S3776 — Extract
HandleRawStartfromRawHelperAccumulator.ConvertTokens(cognitive complexity reduced) - S3776 —
Tokenizer.Parse: extractParseExpressionToken,ReadExtraBraces,ReadExpressionOpening,ReadStaticCharacter; read/peek order unchanged (refactor: address pending SonarCloud S3881/S3776 items in lexer and ReusableStringWriter #679) - S3776 —
LiteralParser: extractAppendEscapeSequenceandIsEndOfLiteral(refactor: address pending SonarCloud S3881/S3776 items in lexer and ReusableStringWriter #679) - CS0618 — Replace all uses of obsolete
Compatibility.RelaxedHelperNaminginPathBinder,HelperConverter,HelperFunctionBinder,HandlebarsConfigurationAdapter
Test quality
- S3415 / xUnit2000 — Fix
Assert.Equal(actual, expected)argument order inCustomConfigurationTests - CS0108 — Add
newmodifier to hiding method inFixedSizeDictionaryTests - Characterization tests
HelperLiteralArgumentsAreParsedcovering literal escapes and non-delimited literal termination, verified against both pre- and post-refactor lexer (refactor: address pending SonarCloud S3881/S3776 items in lexer and ReusableStringWriter #679)
❌ Pending
Breaking / high effort
- S2326 —
IDescriptor<TOptions>: type parameterTOptionsis declared but never used in the interface body. Removing it is a breaking API change requiring a major version bump.
Medium effort
- S3776 —
BlockHelperFunctionBinder.BindByRef: the nestedswitch/ifstructure is tied to expression-tree construction — naive extraction causes runtimeNullReferenceException. Needs a careful approach (see ExpressionShortcuts scope constraint note below). - S3776 —
PartialBinder.VisitPartialExpression: complexity climbed with the partial-indentation feature. Any extraction must keepArg(...)/Cast<T>(...)variables scoped to theCall(() => ...)that uses them.
Remaining SonarCloud findings (not yet triaged)
- The original Sonar report listed 190 code smells total; the work above addressed the ~53 flagged as critical or major. The remaining ~137 lower-priority smells have not been reviewed. A full triage pass against the current SonarCloud dashboard is needed to identify any worth fixing.
Notes
ExpressionShortcuts scope constraint
When refactoring code in files that use the Expressions.Shortcuts library (PartialBinder, BlockHelperFunctionBinder, etc.), variables created with Arg(...), Cast<T>(...), or New(...) must be declared in the same lexical block as the Call(() => ...) lambda that references them. Hoisting them to an outer scope causes runtime InvalidCastException or NullReferenceException because ExpressionShortcuts rewrites the lambda's expression tree at build time and out-of-scope references are not rewritten. Safe to hoist: CompilationContext.Args.BindingContext and CompilationContext.Args.EncodedWriter (these are parameter expressions, not new constant nodes).
Activity
rexm commentedon Nov 8, 2026
/harness adopt
stencil-harness commentedon Nov 8, 2026
I'm the agent managing issue intake on Rex's behalf. I understand this issue tracks remediation of SonarCloud quality and security findings in Handlebars.Net, and I'm checking whether I can work on it autonomously.
stencil-harness commentedon Nov 8, 2026
I'm unable to proceed further on this and am awaiting follow-up from a human maintainer.
stencil-harness commentedon Nov 8, 2026
Picking this up. Plan for this pass: work through the pending items that can be done without breaking anything (
ReusableStringWritersealing, complexity extractions inLiteralParserandTokenizer). TheIDescriptor<TOptions>item (S2326) is a breaking change, so I'll leave it alone. I'll report back with what changed and how it was verified.stencil-harness commentedon Nov 8, 2026
Handlebars-Net/Handlebars.Net: changes are in #679, which is set to merge automatically once CI passes. No behavior or public API changes.
ReusableStringWriteris now sealed. Its only constructor is private, so no outside code can subclass it.Tokenizer:Parseis split into smaller helpers (ParseExpressionToken,ReadExtraBraces,ReadExpressionOpening,ReadStaticCharacter). Characters are still read and peeked in the same order.LiteralParser: escape handling and end-of-literal detection are now separate helpers.Verification: the full test suite passes (2207 tests). New
HelperLiteralArgumentsAreParsedtests cover escaped quotes and how number literals end. They pass on both the old and the new code.Still pending:
IDescriptor<TOptions>: fixing it breaks the API, so it should wait for a major version.BlockHelperFunctionBinder.BindByRef/PartialBinder.VisitPartialExpression: not attempted because of the ExpressionShortcuts scoping risk noted above.Note: #679 says it closes this issue, so merging it will close the tracker even though the items above are still open. Reopen it if you want to keep tracking them.
rexm commentedon Nov 8, 2026
Update the original issue to reflect what's been addressed.
stencil-harness commentedon Nov 8, 2026
I'm unable to proceed further on this and am awaiting follow-up from a human maintainer.
stencil-harness commentedon Nov 8, 2026
Picked up: a session is starting on this.
stencil-harness commentedon Nov 8, 2026
I've updated the issue description to show what #679 completed:
ReusableStringWriter(S3881), and the S3776 complexity cleanups inTokenizerandLiteralParser, each linked to refactor: address pending SonarCloud S3881/S3776 items in lexer and ReusableStringWriter #679.HelperLiteralArgumentsAreParsedtests.Still pending: S2326 (
IDescriptor<TOptions>, which needs a major version), the S3776 cleanups inBlockHelperFunctionBinder.BindByRefandPartialBinder.VisitPartialExpression, and reviewing the remaining lower-priority warnings. No code changed.