QRGuard

QRGuard (ODC)

Stable version 0.4.0 (Compatible with ODC)
Uploaded on 5 Aug by Alexandre Realinho
QRGuard

QRGuard (ODC)

Documentation
0.4.0

INSTALLING QRGUARD IN YOUR ODC APP
------------------------------------

1. Download QRGuard v0.4.0 ExternalLibrary.zip from the OutSystems
Forge.

2. Open ODC Portal, go to Extensibility -> External Logic -> Upload.

3. Select ExternalLibrary.zip. Wait for the upload to finish.

4. Open the ODC Studio app or module that will consume QRGuard.

5. Manage Dependencies -> QRGuard. Tick the four Server Actions and the
three Structures (QRScanBatchResult, QRScanResult,
RedactQRCodesResult).

6. Publish the module. No server-side configuration, Site Property, or
role assignment is required. Every call is stateless.


USING THE SERVER ACTIONS
------------------------

Four Server Actions cover the four common scenarios. All are stateless,
side-effect-free, and safe on any input (non-image inputs return
IsPassthrough=true with no exception).


ContainsQRCode

Fast Boolean shortcut - true when at least one QR is decodable on any
frame. Use for blanket rejection where QR codes are never a legitimate
input (support chatbots, document summarisers, product-image
classifiers).

Inputs
Image Binary Data
Output
Boolean


ScanImage

Full multi-frame + multi-QR scan with per-payload classification. Use
when QR codes may be legitimate (product catalogues, receipt
processors) and you need per-content filtering rather than blanket
rejection.

Inputs
Image Binary Data
CustomInjectionPatterns Text optional; pipe-separated extra
patterns unioned with the 18
built-in list; empty entries
skipped, trimmed, lowercased,
deduplicated
MaxImageDimension Integer 0 = default 4096; positive
clamped to floor 512
SuspiciousLengthThreshold Integer 0 = default 500; positive
clamped to floor 20
Output QRScanBatchResult
ContainsAnyQRCode Boolean
QRCodeCount Integer = QRCodes.Length
AnyIsSuspicious Boolean
QRCodes List of QRScanResult
IsPassthrough Boolean true only when input is not an image
ProcessingError Text
Nested QRScanResult
DecodedContent Text
IsSuspicious Boolean
DetectedPatterns Text pipe-separated matched patterns
FrameIndex Integer 0-based; >0 only for multi-frame
formats


DecodeAllQRCodes

Same shape as ScanImage but pattern filtering is skipped by contract.
Every returned QRScanResult has IsSuspicious=false and
DetectedPatterns="". Use when the caller wants raw payloads for its
own downstream processing.

Inputs
Image Binary Data
MaxImageDimension Integer same semantics as ScanImage
Output
QRScanBatchResult (see above)

RedactQRCodes

Locates every QR's bounding box and overwrites each matching QR with a
solid fill colour. Output re-encoded in the same format as the input
(Jpeg to Jpeg, Png to Png, etc.). Use when QR codes are expected in
the input (receipts, product photos) but suspicious payloads must be
neutralised in place rather than rejecting the whole upload.

Inputs
Image Binary Data
CustomInjectionPatterns Text applies only in "Suspicious"
mode; same semantics as
ScanImage
MaxImageDimension Integer same semantics as ScanImage
RedactMode Text "Suspicious" (default; empty
also = "Suspicious") redacts
only flagged QRs; "All"
redacts every QR regardless of
content and ignores the
length threshold
FillColor Text hex; "#RRGGBB" recommended;
"#AARRGGBB" accepted; empty or
invalid = solid black
SuspiciousLengthThreshold Integer applies only in "Suspicious"
mode
Output RedactQRCodesResult
CleanImage Binary Data sanitised image; original bytes
when IsPassthrough or error
OriginalFormat Text empty when IsPassthrough=true
TotalQRCodeCount Integer
RedactedCount Integer = TotalQRCodeCount in "All" mode
IsPassthrough Boolean
ProcessingError Text


Recommended pipeline

1. Receive the uploaded image as a Binary Data input.
2. Call FileMetadataStrip.StripFileMetadata to remove EXIF/IPTC/XMP.
3. Call StegoGuard.RecompressImage on the CleanFile from step 2.
4. Call the QRGuard action for your feature (pass optional parameters
as "", 0, "", 0 to use defaults; RedactQRCodes uses "Suspicious"
and "#000000" for the two Text defaults):
- ContainsQRCode -> reject if it returns true
- ScanImage -> reject if AnyIsSuspicious is true; if
ContainsAnyQRCode is true, validate each
QRCodes[i].DecodedContent against the
expected format (product SKU, receipt
number, allow-listed URL)
- DecodeAllQRCodes -> inspect QRCodes[i].DecodedContent yourself
- RedactQRCodes -> forward CleanImage; suspicious QRs are
overwritten in place, legitimate QRs
remain intact in "Suspicious" mode
5. Forward the sanitised image to the vision AI API.


CONFIGURATION AND TUNING
------------------------

QRGuard has no Site Properties and no configuration screen. Behaviour
is controlled entirely through the input parameters of each Server
Action.


Injection patterns

18 case-insensitive substring matches are built in: ignore previous,
ignore all previous, disregard previous, disregard all previous,
forget previous, forget all previous, system prompt, new instructions,
you are now, you must now, act as if, pretend you are, pretend to be,
your new role, developer mode, jailbreak, dan mode, do anything now.

Pass extra case-insensitive patterns via CustomInjectionPatterns as a
pipe-separated Text (e.g. "reveal the api key|forget the safety
rules"). Empty entries skipped; each entry trimmed and lowercased;
duplicates deduplicated. Empty string = built-in list only.
DecodeAllQRCodes deliberately ignores this parameter.


Suspicious-length threshold

Decoded content longer than SuspiciousLengthThreshold is flagged as
suspicious regardless of pattern match. Long natural-language QRs are
the most common prompt-injection carrier.

0 = default 500 chars
Positive value = custom threshold, clamped to floor of 20
Very high value = effectively disables the length check

Applied by ScanImage and by RedactQRCodes in "Suspicious" mode.


Downscaling cap

Large images are downscaled per-frame before ZXing scans. Aspect
ratio preserved.

0 = default 4096 pixels (wider or taller)
Positive value = custom cap, clamped to floor of 512
On RedactQRCodes = bounding boxes are mapped back to the original
resolution before painting, so output
dimensions are preserved


Redaction fill colour

"#RRGGBB" recommended, e.g. "#000000" black, "#FFFFFF" white
"#AARRGGBB" with alpha channel
"" solid black
invalid solid black (redaction is never silently skipped)


Passthrough contract

QRScanBatchResult.IsPassthrough and RedactQRCodesResult.IsPassthrough
are true when the input is not a recognised image (plain text, PDF,
arbitrary bytes, empty buffer). False when the input is an image,
regardless of whether any QR was found and regardless of whether
ProcessingError is non-empty. Use this to distinguish "not an image"
from "we looked and found nothing".


Error handling

All four actions swallow decode-level exceptions and populate
ProcessingError with a human-readable message. Log ProcessingError for
diagnostics; when non-empty, treat the input as unknown and fail-
closed in security-sensitive flows.


Security pipeline position

Layer 1, Priority 4. Run after FileMetadataStrip (Priority 1) and
StegoGuard (Priorities 2, 3), before sending to any vision-capable AI
API. QR codes survive JPEG recompression and Gaussian filtering thanks
to Reed-Solomon error correction, so QRGuard is a necessary complement
to StegoGuard - one cannot substitute for the other.


SUPPORTED FILE FORMATS
----------------------

Every format is covered by a per-format xUnit regression test (479
ODC tests, part of a 1,322-test matrix).

Full QR round-trip via Magick.NET-Q8 (30 formats): JPEG, PNG (Png8/
24/32/48/64/00 subformats regression-locked), GIF, BMP, TIFF, WebP,
AVIF, JXL, JP2, J2C, PSD, PSB, DDS, EXR, HDR, QOI, DPX, CIN, SGI, SUN,
PCX, PBM, PGM, PPM, PNM, FITS, XBM, XPM, MSVG, DIB, DCX.

Multi-frame containers (QR detected on any frame, FrameIndex
reported): animated GIF/WebP, multi-frame TIFF, APNG, MNG, DCX.

Recognised without full decode (EmptyBatchResult): TGA, WBMP, ICO.

Synthetic-byte passthrough: HEIC/HEIF/AVIF (ftyp brands), MPO, SVG,
UHDR, camera-RAW (DNG/CR2/NEF/ARW/PEF/ORF/RAF/X3F), JXR, JBIG, XCF,
WMF, AI, PCD, PICT, DICOM.

Non-image inputs (plain text, PDF, DOCX, empty) return
QRScanBatchResult with IsPassthrough=true, ContainsAnyQRCode=false,
empty QRCodes, empty ProcessingError.