fix(security): expand MagicByteValidator beyond image-only allowlist - #626
Merged
Conversation
PR #601 added the audience-gated ChannelAttachmentPolicy that allows PDFs, Office documents, archives, and media for Team/Personal audiences — but MagicByteValidator's hardcoded AllowedExtensions dictionary still only accepted PNG/JPG/GIF/WebP, rejecting everything else at ingress with "File extension '.pdf' is not allowed". The SlackAttachmentIngressTests suite used NullContentScanner by default, so the Pdf_in_dm_* and Docx_in_dm_* tests only exercised the policy layer and never saw the real scanner. Rewrite MagicByteValidator around a signature-rule table keyed by declared MIME. Support every category the Team audience advertises: PDF, OOXML/ODF, legacy OLE Office, plain/structured text, RTF, zip/7z/rar/gzip/bzip2/xz, and mp3/mp4/wav/ogg/avi/webm/mkv. Harden each matcher beyond minimum magic — validate PDF version digit, ZIP exact header pair, gzip DEFLATE method, bzip2 BCD-Pi block header, RAR v4/v5 variant tail, ISO BMFF box size + printable-ASCII major brand, Ogg version byte, ID3v2 major version, MP3 strict 12-bit sync plus reserved-layer check. Seed ContentPolicy.DefaultAllowedMimeTypes from the validator's supported set so the two layers can't drift, and raise DefaultMaxFileSizeBytes from 20 MB to 25 MiB to match ChannelAttachmentPolicy. Flip SlackAttachmentIngressTests.BuildGateway to default to the real MagicByteContentScanner so the existing Pdf_in_dm_* and Docx_in_dm_* regression tests now actually exercise production behavior. Add a PlainText_in_dm_* ingress test, 13 MagicByteValidator category happy paths, and 15 adversarial polyglot-rejection tests for the hardened matchers.
Aaronontheweb
marked this pull request as ready for review
April 13, 2026 10:40
Aaronontheweb
enabled auto-merge (squash)
April 13, 2026 10:40
This was referenced Apr 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
D0AC6CKBK5K/1776042979.764809rejected a PDF withContent scanner rejected … File extension '.pdf' is not allowed. Root cause: PR feat(channels): audience-gated attachment ingress contract + Slack PDF support #601 addedChannelAttachmentPolicyaudience gates that allow PDFs, OOXML, archives, and media — butMagicByteValidatorwas still hardcoded to PNG/JPG/GIF/WebP, so everything else was rejected at the scanner layer after passing the policy layer.MagicByteValidatoraround a signature-rule table keyed by declared MIME. Support every category theTeamaudience advertises: PDF, OOXML (docx/xlsx/pptx), ODF, legacy OLE Office, plain/structured text, RTF, zip/7z/rar/gzip/bzip2/xz, and mp3/mp4/m4a/wav/ogg/avi/webm/mkv. SeedContentPolicy.DefaultAllowedMimeTypesfromMagicByteValidator.GetSupportedMimeTypes()so the two layers can't drift again, and raiseDefaultMaxFileSizeBytesfrom 20 MB to 25 MiB to matchChannelAttachmentPolicy.DefaultMaxFileBytes.GIF8[79]a, PDF requires a version digit + dot, RTF requires a version digit, ZIP enforces exact header-pair combinations, gzip requires the DEFLATE compression method, bzip2 validates the full 10-byte BCD-Pi block header, RAR requires the v4 (00) or v5 (01 00) variant tail, ISO BMFF validates box size + printable-ASCII major brand, Ogg requires version byte0x00, and MP3 uses strict 12-bit sync plus reserved-layer rejection and ID3v2 major-version validation.SlackAttachmentIngressTests.BuildGatewayto default to the realMagicByteContentScannerinstead ofNullContentScanner. This is the test-harness gap that allowed PR feat(channels): audience-gated attachment ingress contract + Slack PDF support #601 to ship — the existingPdf_in_dm_*andDocx_in_dm_*regression tests now exercise production behavior. Adds aPlainText_in_dm_*ingress test and 15 adversarial polyglot-rejection tests for the hardened matchers.Test plan
dotnet test src/Netclaw.Security.Tests— 259/259 passing (+37 new cases)dotnet test src/Netclaw.Actors.Tests --filter "FullyQualifiedName~Slack"— 147/147 passingdotnet testfull suite — all greendotnet slopwatch analyze— 0 issues[attachment]lines withpath="inbox/<name>.<ext>"for all four; PDF inlined asDataContenton vision-capable models; docx/zip/mp4 path-only withFormatNotInlineable. Files present undersessions/<sessionId>/inbox/..exerenamed to.pdf→ expectExecutableContentrejection. Send a PNG renamed toreport.pdf→ expectMimeTypeMismatchrejection.