fix(security): close codeql findings #1 and #2

Two CodeQL alerts opened against the codeql-manual-build workflow's
first scan. Both real, both small fixes.

#1 Medium / Workflow does not contain permissions
   build.yml runs read-only against the repo (no push, no release
   creation, no API mutations) but never declared a permissions
   block, so the default GITHUB_TOKEN scope applied. Pin to
   contents: read at workflow level. Release and CodeQL workflows
   already have their explicit minimal scopes.

#2 Critical / Unvalidated local pointer arithmetic
   ImGuiUtil.WrappedTextWithPos splits its input on newlines and
   passes each part through Encoding.UTF8.GetBytes inside a fixed
   block. Empty splits (consecutive newlines, blank lines) produced
   a zero-length byte array, fixed gave us a valid pointer, and
   textEnd = text + bytes.Length collapsed onto text. The downstream
   ImGuiNative.CalcWordWrapPositionA calls received identical start
   and end pointers, which is undefined behaviour at the native
   boundary even if it happens to no-op on the current ImGui build.
   Bail before entering the fixed block when bytes.Length == 0 and
   render an empty line for the gap, which is what the original
   text == null guard was trying to do but could never reach inside
   a fixed block over a non-null array.
This commit is contained in:
2026-05-02 23:25:41 +02:00
parent 6e8fcc8cc3
commit 53c432a635
2 changed files with 20 additions and 7 deletions
+13 -7
View File
@@ -94,18 +94,24 @@ internal static class ImGuiUtil
foreach (var part in csText.Split(["\r\n", "\r", "\n"], StringSplitOptions.None))
{
var bytes = Encoding.UTF8.GetBytes(part);
// Empty splits (consecutive newlines) leave bytes.Length at 0
// and the textEnd pointer below would coincide with text. The
// ImGuiNative word-wrap calls treat that as undefined input,
// and the CodeQL "unvalidated local pointer arithmetic" alert
// also flags it. Render an empty line and skip the unsafe
// block entirely for this iteration.
if (bytes.Length == 0)
{
ImGui.TextUnformatted("");
continue;
}
fixed (byte* rawText = bytes)
{
var text = rawText;
var textEnd = text + bytes.Length;
// empty string
if (text == null)
{
ImGui.TextUnformatted("");
continue;
}
var widthLeft = ImGui.GetContentRegionAvail().X;
var endPrevLine = ImGuiNative.CalcWordWrapPositionA(ImGui.GetFont().Handle, ImGuiHelpers.GlobalScale, text, textEnd, widthLeft);
if (endPrevLine == null)