Two opt-in UX features. Existing users see no change unless they
enable the new toggles.
- Pop-out input: global master switch in Settings → Window → Frame.
When enabled, every pop-out window grows a compact input bar
(channel-coloured icon + text input). Independent text buffer and
history cursor per pop-out; channel changes apply globally.
- Chat colour presets: seven built-ins above the per-channel colour
list — ChatTwo Default, High-Contrast, Pastell, Dark-Mode-Tuned,
Hellion (brand), Night Blue (bonus), Indigo Violet (bonus).
Configuration migrates from v10 to v11 with a diagnostic log.
Closes the remaining gaps in GitHub's community-standards check, adds
explicit privacy and dependency documentation matching the plugin's
"DSGVO-by-design" claim, and removes the stale upstream Crowdin
artefact so the repo no longer suggests it ships its own translation
pipeline.
New community-health files:
- CODE_OF_CONDUCT.md: project-specific, short and direct, single
reporting path to kontakt@hellion-media.de
- CONTRIBUTING.md: scope, accepted vs declined contributions, build
and test instructions, EUPL-1.2 contribution terms, translation
policy split between Hellion-specific (here) and upstream strings
(Chat 2 repo)
- SUPPORT.md: routing for bugs, security, privacy and casual feedback
- .github/PULL_REQUEST_TEMPLATE.md: summary, change-type checklist,
testing notes, compatibility notes for migrations and manifest
fields, contribution checklist
- .github/FUNDING.yml: comments-only file, no platforms enabled,
points donors at the upstream Chat 2 maintainers' Ko-fi pages
New privacy and compliance documentation:
- PRIVACY.md: what the plugin stores locally (config, SQLite,
EmoteCacheV1), retention defaults, the two outbound network calls
(BetterTTV API+CDN with ShowEmotes opt-out, Square Enix Lodestone
font once-off), explicit no-telemetry statement, GDPR
Art. 15/17/18/20/21 rights mapped to plugin features, third-party
privacy-policy links
- THIRD_PARTY_NOTICES.md: direct NuGet dependencies with versions
pinned to v0.5.4 (MessagePack, Microsoft.Data.Sqlite, morelinq,
Pidgin, SixLabors.ImageSharp under Six Labors Split License 1.0),
Dalamud SDK and .NET tooling, bundled Exo 2 font (OFL-1.1) and
plugin icon, network-touch status per component, re-audit commands
Crowdin cleanup:
- crowdin.yml deleted (was upstream Chat 2's project_id 663694,
pointed at /ChatTwo/Resources/Language.resx, never wired to
HellionChat strings)
- README, CONTRIBUTING and CODE_OF_CONDUCT no longer suggest
HellionChat operates a Crowdin project; remaining mentions are
explicitly framed as upstream Chat 2's workflow
Contact and version consistency:
- Maintainer email switched from maintainer@hellion-media.de to
kontakt@hellion-media.de in SECURITY.md and NOTICE.md
- README version references updated to 0.5.4 (header, project status
block) and the update-tag pattern generalised from v0.1.x to v0.X.Y
- bug_report.yml version placeholder bumped to 0.5.4
- Project-documents table added to README footer linking all health
and reference files in one place
Release-body automation:
- .github/workflows/release.yml now extracts the matching version
block from ChatTwo/HellionChat.yaml's changelog and combines it
with a static install / docs footer (custom-repo URL, project
document links, licence) before passing the result to
softprops/action-gh-release@v3 via body_path
- Workflow fails fast if no changelog block exists for the tagged
version, automating the existing "yaml + repo.json + release body
kept in sync" rule
- Tag value passed via env: TAG_NAME with strict ^v\d+\.\d+\.\d+$
validation before any string concatenation, so the tag input cannot
break out into shell evaluation
The pointer-arithmetic CodeQL alert kept re-firing on each shape of
the previous shallow fix because Encoding.GetBytes is virtual and
every length value derived from its return inherited the taint.
Refactor the routine to thread int offsets through index-based
control flow and only compute pointers inside two small helpers
(CalcWordWrap and DrawText) that take an already-pinned base pointer
plus offsets sourced from local logic, not from any virtual return.
Buffer is now allocated against Encoding.UTF8.GetMaxByteCount via
ArrayPool with a real 16 KiB upper bound, and the encoded length
returned by GetBytes is validated against that ceiling before
anything touches the pointer. Behaviour is byte-identical to v0.5.3,
verified locally with the same input shapes the previous code path
handled.
Slim changelog: trimmed the per-version blocks down to v0.5.1-v0.5.4
plus a link to GitHub releases for older history. The previous block
ran ~9000 characters and was dragging the manifest payload down for
no benefit; users see the latest release block first anyway.
Single-fix patch to close the CodeQL pointer-arithmetic alert that
v0.5.2 left open. v0.5.2 already shipped, so we tag forward instead
of moving the published tag.
CodeQL re-opened the unvalidated-pointer-arithmetic alert at the new
textEnd line because Encoding.GetBytes is a virtual method on
Encoding and the returned array's Length is therefore tracked as
untrusted input for pointer arithmetic.
Compute the expected byte count from the same encoder via
GetByteCount and bail out if the actual buffer length does not match.
That is a real consistency check that would catch a maliciously
swapped Encoding.UTF8 instance, not a dead defensive guard. The
empty-split early-out from the previous fix is folded into the same
condition.
Three packaging defects rolled into one fix:
- The custom DalamudPackager.targets override forced HandleImages and
ImagesPath through the legacy code path. SDK 15 handles images by
default and the override produced an output manifest with neither
IconUrl nor ImageUrls populated. Removed.
- The csproj only included images/icon.png explicitly via
<None Include>, so chatWindow.png and withSimpleTweaks.png never
reached the build output and never made it into the release ZIP
either. Switched to a glob include.
- HellionChat.yaml carried no icon_url / image_urls, so even after
the SDK started writing the manifest correctly, both fields stayed
unset. Added them pointing at the public raw.githubusercontent
URLs that already work for the repo.json IconUrl.
Net effect on a fresh release: Dalamud picks up the icon next to the
DLL on dev installs, the plugin-installer card shows the proper
HellionChat logo for users coming through the custom repo, and the
two screenshot images are listed alongside the description so the
plugin installer carousel works the way other Dalamud plugins look.
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.
Three real-world adjustments to the default config that ships with a
fresh install:
- HellionThemeWindowOpacity 0.92 -> 0.5 so a fresh install lands at
the more glass-like default the maintainer uses daily
- Use24HourClock false -> true to match a German / European locale.
Works correctly thanks to the v0.5.1 strict-format fix that uses
CultureInfo.InvariantCulture instead of the host culture
- HellionParty preset Channel: InputChannel.Party -> null. Auto-
routing /party into a tab that also collects /alliance and /pvpteam
surprises the user when they wanted to type into the other ones;
the tab stays as a read surface
LoadPreviousSession and FilterIncludePreviousSessions stay false to
keep the privacy-strict 'every session starts fresh' line. The
maintainer's personal settings flip them on, but that's an
opt-in choice, not a default we should ship to every fresh install.
RetentionEnabled also stays false for the same opt-in reason.
Four defaults now match what a daily-driver Hellion install ends up at
anyway, so a fresh install does not feel like the wrong product:
- HellionThemeWindowOpacity 0.92 -> 0.5 (more glass-like)
- LoadPreviousSession + FilterIncludePreviousSessions false -> true
(tabs pick up where they left off after a crash or restart). The
privacy filter still gates what goes into the store; loading what
is already in there is not an additional privacy cost.
- Use24HourClock false -> true (matches a German / European locale,
works with the strict CultureInfo.InvariantCulture format from the
v0.5.1 fix).
RetentionEnabled stays at false because that one is a documented
opt-in privacy line, not a UX default. The persistent retention sweep
should require an explicit user gesture even though my own install
has it on.