Skip to content

Join continuation lines in a builder - #379

Merged
iderex merged 1 commit into
masterfrom
scan/the-continuation-join-is-a-builder
Sep 7, 2026
Merged

iderex merged 1 commit into
masterfrom
scan/the-continuation-join-is-a-builder

Conversation

@iderex

@iderex iderex commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Refs #375, the class cs/string-concatenation-in-loop: one alert, in the continuation join of the script reader ReleaseVersionAgreementTests walks a step's shell with.

Finishes: #375

The site

Read from the platform at the head this branch starts from:

git rev-parse origin/master
e941800b27ee2944bc0759cef29a403e4f98e00d

gh api repos/Flowfin/jellyfin-plugin-metadata-sync/code-scanning/alerts/237 --jq '"#\(.number) \(.state) \(.rule.id) \(.most_recent_instance.location.path):\(.most_recent_instance.location.start_line) sha=\(.most_recent_instance.commit_sha[0:7])"'
#237 open cs/string-concatenation-in-loop Jellyfin.Plugin.MetadataSync.Tests/ReleaseVersionAgreementTests.cs:737 sha=e941800

The line is the pending += ... inside Script, which glues the halves of a shell line ending in a backslash back together before the readings above it look for a refusal or an assignment.

What changed

pending is a StringBuilder. A continuation line appends its text and a space, a complete line is appended and the joined text taken out, and the builder is cleared rather than replaced. What the reader returns does not move, and nothing outside this one function is touched.

Proved by breaking it

The suite is what says the join kept its meaning, on both lines:

dotnet build Jellyfin.Plugin.MetadataSync.sln --nologo
VSTEST_CONNECTION_TIMEOUT=600 DOTNET_CLI_UI_LANGUAGE=en dotnet test Jellyfin.Plugin.MetadataSync.Tests/Jellyfin.Plugin.MetadataSync.Tests.csproj --nologo --no-build -f net9.0
Failed: 0, Passed: 1709, Skipped: 0, Total: 1709
... -f net10.0
Failed: 0, Passed: 1709, Skipped: 0, Total: 1709

Then the two ways this rewrite could go wrong, each done to the reader and restored, with the two route classes run on net9.0:

the continuation branch made unreachable, so nothing is joined   -> Failed: 1, Passed: 18: TheAssemblyIsRefusedWhenItIsStampedWithAnotherVersion
the space between the two halves dropped                         -> Failed: 0, Passed: 19
restored                                                         -> Failed: 0, Passed: 19

The first is red on the route itself rather than on a fixture: the assembly step in publish.yaml reads its assignment across a continuation, so a reader that stops joining takes half a command as the assignment's origin and the refusal is read as comparing something else.

The second stays green, and that is disclosed rather than repaired. Every reading downstream of the join tolerates the missing space: the refusal patterns allow zero whitespace at every place a continuation would split them, and an origin is a substring read. So the space is held by nothing, and a fixture that made it bite would have to split a line in a place no shell script splits one, which would be a fixture for the reader and not for the route. What holds the space is the reading.

The means

C#, in the file the alert names. StringBuilder is in the base library and the test project already uses it in five other files. The alternative was a dismissal; the sibling board that met this class took the same repair, and the query read the site correctly.

What this does not say

The alert closes when the analysis next runs over master, not on this merge, so #375 stays open until that reading is pasted into it. Whether this class arrives again is operations#2176's question and not this change's.

No second reader looked at this. The runs above are the evidence in place of one.

Code scanning reported the script reader in ReleaseVersionAgreementTests
for concatenating the halves of a continued shell line with + inside its
loop, alert 237 of the class cs/string-concatenation-in-loop. The pending
half is a StringBuilder now, appended to and cleared rather than replaced,
and what the reader returns does not move.

The join is held by the route itself and not only by a fixture: the
assembly step in publish.yaml reads its assignment across a continuation,
so a reader that stopped joining was red on the real file. The space
between the two halves is held by nothing, because every reading after
the join tolerates its absence, and that is written down rather than
given a fixture that would split a line where no script does.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex iderex added the security label Sep 7, 2026
@iderex
iderex merged commit 753547b into master Sep 7, 2026
14 of 15 checks passed
@iderex
iderex deleted the scan/the-continuation-join-is-a-builder branch September 7, 2026 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant