stegoguard
Reactive icon

StegoGuard

Stable version 0.1.2 (Compatible with OutSystems 11)
Uploaded
 on 2 Aug
 by 
0.0
 (0 ratings)
stegoguard

StegoGuard

Documentation
0.1.2
INSTALLING THE EXTENSION
------------------------

1. Download the StegoGuard extension from the OutSystems Forge.

2. Open Service Studio, open your app or module, and go to the Manage Dependencies dialog (Ctrl+Q).

3. Locate the StegoGuard extension in the list, tick the RecompressImage action and the StegoGuardResult and StegoAuditResult structures, then click Apply.

4. Publish the module. No server-side configuration is required.

If you are deploying to your own O11 environment for the first time:

1. Sign in to Service Center on the target environment.

2. Go to Factory > Extensions and upload StegoGuard.xif (or install via LifeTime).

3. Publish the extension. All native dependencies (Magick.NET managed and native DLLs, the Microsoft VC++ 2015-2022 x64 runtime, and four BCL shim assemblies) ship inside the XIF; there is nothing else to install on the server.


CONFIGURATION
-------------

The extension has no Site Properties, no configuration screen, and no per-tenant settings. Every call is stateless. Behaviour is controlled entirely through the RecompressImage input parameters.

Two-engine architecture (automatic, no configuration)

StegoGuard on O11 ships with two image-processing engines and picks between them automatically at runtime:

Primary engine - Magick.NET. Used on all healthy O11 hosts. Delivers the full 52-format matrix (JPEG, PNG, GIF, BMP, TIFF, WebP, AVIF, HEIC, JXL, JPEG 2000, PSD, camera RAW, and more).

Fallback engine - System.Drawing (GDI+). Used only on O11 hosts where the Magick.NET native runtime cannot initialise (OutSystems Personal Environment sandbox on outsystemscloud.com is the currently known case). The extension detects the initialisation failure on the first call, latches an AppDomain-scoped flag, and routes every subsequent call through GDI+. GDI+ (gdiplus.dll) is a Windows built-in KnownDLL and is not subject to the native-code loader policies that block the Magick.NET native runtime.

Both engines produce the same StegoGuardResult and StegoAuditResult contracts. The only visible difference is the supported-format matrix on the fallback engine - see the Supported File Formats section below.


USAGE
-----

Call RecompressImage at the earliest point in any flow that accepts an image upload, before the image is forwarded to a vision-capable AI API, stored, or processed further. Chain it after FileMetadataStrip for defence in depth.

Input
RawImage (Binary Data) - the uploaded image in any supported format. Pass the Content field from a Upload widget or a binary variable you have already loaded.

Quality (Integer) - JPEG quality for the recompression step on the range 1 to 100. Recommended value 85 for general use, 75 for stronger disruption. Values of 0 or less default to 85. Values above 100 are clamped to 100. Applies to every output format: for lossless targets (PNG, TIFF, WebP, and so on) Quality still controls the intermediate JPEG encode that drives the disruption.

OutputFormat (Text) - controls the container format of the output.
Empty string (default) preserves the input format. "JPEG" (also "JPG", case-insensitive) forces JPEG and gives the strongest disruption. "PNG" forces PNG and preserves transparency. Any other value falls back to preserving the input format.

Sigma (Decimal) - Gaussian blur sigma applied after the JPEG round-trip to disrupt spatial-domain payloads that survive the frequency-domain step. 0 (default) disables the filter. 0.5 is recommended for general use, 1.0 for high-security contexts. Values above 1.5 may visibly soften fine detail. Negative values are treated as 0.

Output - StegoGuardResult structure

CleanImage (Binary Data) The re-encoded image with pixel-level, frequency-domain, and alpha-channel steganographic payloads disrupted. Use this in place of the original when calling AI APIs.

OriginalFormat (Text) The format detected on the input (Jpeg, Png, Gif, Bmp, Tiff, WebP, and so on). Empty when IsPassthrough is True. Note that PSB is reported as Psd (shared 8BPS magic) and JPEG-2000 code streams are reported as Jp2.

OutputFormat (Text) The format of the output image. Empty when IsPassthrough is True.

IsPassthrough (Boolean) True when the input is not a recognised image format (PDF, DOCX, plain text, and so on). The original bytes are returned unchanged. Use this in audit logs to distinguish passthrough files from actively disrupted images.

ProcessingError (Text) Non-empty when recompression failed due to a decode or encode error, or when the GDI+ fallback engine is active and the input
is a recognised image in a format GDI+ cannot decode. The original bytes are returned in CleanImage and IsPassthrough is False. Log this value for diagnostics.

StegoAudit (StegoAuditResult) Detection audit for the input image (see below).

Output - StegoAuditResult structure

DetectedType (Text) "RGB pixel LSB", "Alpha-channel LSB", "RGB + Alpha pixel LSB", or empty string when no LSB signature is detected.

Confidence (Decimal) Detection confidence on the range 0.0 to 1.0. Values above 0.7 indicate likely LSB steganography. High-frequency natural photographs can produce elevated confidence without any embedded payload; always review the value before blocking an upload.

RedLsb1Fraction, GreenLsb1Fraction, BlueLsb1Fraction (Decimal) Fraction of pixels in each RGB channel whose least-significant bit is 1. Values near 0.5 are suspicious; values near 0.0 or 1.0 are clean.

AlphaLsb1Fraction (Decimal) Same for the alpha channel. Returns -1.0 when the image has no alpha channel.

GaussianSigmaApplied (Decimal) The sigma value actually used for the post-recompression Gaussian filter. 0.0 when Sigma was 0 or negative, or when IsPassthrough is True.

Recommended pattern in a Server Action:

1. Receive the uploaded image as a Binary Data input parameter.

2. Call OSFileMetadataStrip.StripFileMetadata first to remove EXIF, IPTC, and XMP metadata.

3. Call RecompressImage passing the CleanFile from step 2 as RawImage, Quality = 85, OutputFormat = "", Sigma = 0.5.

4. Use RecompressImage.CleanImage for all downstream calls (AI API, file storage, database).

5. If RecompressImage.StegoAudit.Confidence > 0.7 or DetectedType is non-empty, write RecompressImage.StegoAudit to your audit log.

6. If RecompressImage.ProcessingError is non-empty, log it and either block the upload or fall back to a safe default response.


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

Every format listed below has an explicit xUnit test in the component's test project. No format is claimed here that is not verified by a regression test.

Primary engine (Magick.NET, all healthy O11 hosts)

Standard raster images (JPEG, PNG, GIF, TIFF, WebP, BMP) The JPEG round-trip is applied to the RGB channels; the alpha channel is separated and put through its own grayscale JPEG round-trip so transparency is preserved with at most about 1 percent per-pixel opacity change. Animated GIFs, animated
WebPs, and multi-frame TIFFs are disrupted frame by frame; animation metadata (delay, iterations, disposal method) is preserved.

PNG subformats (Png8, Png24, Png32, Png48, Png64, Png00) All six subformats route through the standard PNG pipeline. The Q8 build silently downsamples 16-bit-per-channel subformats (Png48, Png64) to 8-bit samples but still writes a valid PNG.

Camera RAW (DNG, CR2, NEF, ARW, ORF, PEF) Decoded via the built-in dcraw path; the demosaiced output is transcoded to JPEG (or the requested OutputFormat). RAF and X3F fall through to Passthrough on the current Magick.NET-Q8 build.

Advanced raster (JPEG 2000, JPEG XL, AVIF, PSD, PSB, DDS, DPX, CIN, HDR, EXR, FITS, QOI, SGI, SUN, PCX, DCX, XPM, XBM, PBM/PGM/PPM/PNM, MSVG, UHDR, MNG, MPO) Fully supported round-trip through the recompression pipeline. JPEG-2000 code streams (J2c, J2k) are reported as Jp2 by Magick.NET on both read and write. PSB shares magic bytes with PSD and is reported as Psd.

Multi-frame (Animated GIF, Animated WebP, Multi-frame TIFF, APNG, MNG, DCX, MPO) Each frame is disrupted independently and animation metadata is preserved. Animated WebP requires the libwebpmux native library; without it only the first frame is disrupted and trailing frames are dropped.

Vector (SVG, MSVG) Rasterised at their intrinsic resolution and disrupted like any other raster. The vector source is not preserved in the output. If preserving SVG is important, strip metadata via FileMetadataStrip and skip StegoGuard.

Palette (GIF) Recompression relies on palette re-quantization. Classical LSB disruption is not applicable but re-quantization still perturbs pixel values. Animated GIFs are disrupted frame by frame.

HEIC / HEIF (mif1) HEIC is decoded via the libaom AV1 fallback and transcoded to JPEG on write because the HEVC encoder is GPL-licensed. Synthetic HEIF mif1 ftyp bytes fall through to Passthrough on the current Windows Magick.NET-Q8 build.

Returned as passthrough on the current Magick.NET-Q8-AnyCPU build (Windows): TGA, ICO, PCD, PICT, JXR/WDP, DCM, RAF, X3F, XCF, JBIG, WBMP, WMF. Bytes are returned unchanged; the codec either cannot decode the input on this build (JXR, DCM, RAF, X3F, XCF, WBMP), or Magick.NET's auto-detection does not recognise the format from raw bytes (TGA, JBIG, WMF, PICT, ICO, PCD).

Adobe Illustrator (AI), PDF, and non-image inputs (Office documents, plain text, JSON, XML, audio, video) Returned unchanged with IsPassthrough set to True. Combine with FileMetadataStrip for the container metadata surface.

Fallback engine (System.Drawing / GDI+, locked-down hosts only)

The fallback engine partitions every call into one of three deterministic outcomes:

Actively processed - JPEG, PNG (with alpha), GIF, animated multi-frame GIF, BMP, TIFF, multi-page TIFF. Full JPEG round-trip, optional Gaussian filter, and per-channel LSB audit all applied normally.

Error result - WebP, HEIC, HEIF, AVIF, JXL, JPEG 2000, camera RAW (DNG, CR2, NEF, ARW, ORF, PEF), PSD/PSB, DDS, DPX, CIN, EXR, FITS, HDR, MNG, MPO, MSVG, SVG, QOI, SGI, SUN, PCX, DCX, XPM, XBM, PBM/PGM/PPM/PNM, JBIG, XCF, WMF, JXR/WDP, AI, ICO, and all other exotic formats. GDI+ cannot decode these; the call returns IsPassthrough=False, CleanImage = original bytes, and a non-empty ProcessingError explaining the fallback limitation. The caller receives an explicit failure signal instead of a silent passthrough - handle it in the Server Action (log and reject, or retry via an alternate pipeline).

Passthrough - non-image inputs (PDF, DOCX, plain text, and so on) return IsPassthrough=True and empty ProcessingError, identical to the primary engine.


NOTES
-----

Defaults - When Quality is 0 or negative it is treated as 85. When Sigma is negative it is treated as 0. Empty or unrecognised OutputFormat values preserve the input format.

Format normalisation - Magick.NET uses a canonical name for shared codecs. PSB is reported as Psd (shared 8BPS magic; differing only in the version field). JPEG-2000 code streams (J2c, J2k) are reported as Jp2 (both share the JP2 codec on the read path). The Ai format is reported as Pdf when the file uses the modern PDF-based AI container.

Multi-frame - When the input has more than one frame and the target format supports animation (GIF, TIFF), every frame is individually put through the JPEG round-trip and animation metadata is preserved. On the GDI+ fallback engine, animated GIF frames are recompressed but per-frame delay metadata is not preserved - the output uses default GIF timing.

Empty and single-byte inputs - Returned as Passthrough with an empty ProcessingError and StegoAudit.AlphaLsb1Fraction set to -1.0.

Decode errors - When the active engine recognises the format but the decode or encode step throws, the original bytes are returned in CleanImage, ProcessingError contains the error message, IsPassthrough is False, and StegoAudit is populated on best effort. Log ProcessingError and consider blocking the upload.

Detecting the active engine - Callers do not need to know which engine is active. The StegoGuardResult contract is identical on both. To distinguish a fallback-engine error result from a normal processing error, inspect ProcessingError: the fallback engine begins its message with "Image format '<X>' is not supported by the GDI+ fallback engine".

Security pipeline position - Layer 1, Priorities 2 and 3. Run after OSFileMetadataStrip and before sending to any vision-capable AI model. Consider OSQRGuard alongside to cover QR-code prompt injection, which survives JPEG recompression because of the Reed-Solomon error correction built into QR.