We believe a photographer should be able to see how the software guarding their life's work actually holds up — not take our word for it. So here it is: the security audits we've run on SnapSmack and its companion tools, the findings, and the releases that closed them.

Transparency and accountability aren't a marketing line here, they're the deal. Claude and Codex run ongoing security audits of the codebase; high and medium-risk findings are fixed immediately, low-risk ones on a schedule. The reports below are the closed ones — issues found, issues fixed, dated and signed. What you won't find is a report describing a live, serious hole that's still open: publishing the blueprint for an unpatched break-in would put every SnapSmack site at risk, which is the opposite of protecting you. Those stay private until they're fixed, and then they show up here. That's responsible disclosure, and it's the honest version of "we take security seriously."

"Buzzers"? Victorian thieves' cant. To buzz was to pick pockets, and a buzzer was the pickpocket — the one working the crowd for whatever wasn't nailed down. This is the page where we show our work against the buzzers.

Closed Audits

  1. Aug 27llms.txt — Agent Instructions, Executable References & Publisher Data
  2. Aug 25Multisite Federation — Cross-Blog Trust, the Public Directory & SNAP SLAPPER
  3. Aug 21CMS Architecture Assessment — Direct-Access Guards & Content-Model Integrity
  4. Aug 18CMS Compliance, Image Handling & the Missing Content Invariant
  5. Aug 15Live Penetration Test — Installer, Bootstrap, JSON-LD & Editor Boundary
  6. Aug 14Full Multi-Dimension Audit — Authentication, Recovery, Federation & Uploads
  7. Aug 14Audit Process Correction — Stale-Branch Simulation & Clean Release Delta
  8. Aug 13Pixelix Live Test & Release Lifecycle
  9. Aug 13THE HUB Shared Stores & Launcher Boundary
  10. Aug 13Pixelix Posting & OAuth for GRAMOFSMACK
  11. Aug 7Desktop Tools — What "Already Fixed" Was Hiding
  12. Aug 7Download Links — Escaping Is Not Validation
  13. Aug 7Smack Up Your Backup’s First Review — Closed at Last
  14. Aug 6FLKR FCKR — Credentials at Rest, in Transit, and Trusted Paths
  15. Aug 6GET YOUR SHIT SORTED — Desktop Trust Boundary & API Key Lifetime
  16. Aug 5Gallery Skins — JavaScript Package Boundary
  17. Aug 4Smack Up Your Backup Desktop — Credential Vault & Transport
  18. Aug 4SmackPress WordPress Migration — Import Sanitising
  19. Jul 27IP SMACKER & Login Shield — Client-Address Spoofing
  20. Jul 24Skin Manifest RCE & Credentials Export — Closure
  21. Jul 15Fediverse Client — Attack Surface
  22. Jul 4Fediverse Search — Token Isolation
  23. Jun 29Cloud Backup From the Web Host — Feature Removed
  24. Jun 28Skin Manifests Could Run Code — The Finding
  25. Jun 27Cross-Site Request Forgery — Closed Site-Wide
  26. Jun 25Son of a Batch — Batch Poster Review
  27. Jun 20Dev-File Leak & SMACKBACK Blind Spot
  28. Jun 20Auto File-Deletion Attack Surface
  29. Jun 19SMACKBACK Unknown-File & Cron-Verify Gap
  30. Jun 18Installer Constants & Skin Attack Surface
  31. Jun 17Skin Inline-Script Manifest Bypass
  32. Jun 15Mesh Roster Key Broadcast
  33. Jun 12Imported-Caption XSS — Remediation
  34. Jun 12SYBU Recovery & Unzucker Captions
  35. Jun 7Unzucker Attack Surface
  36. Jun 7Canonical-Schema Fetch Hardening
  37. Jun 5SMACKBACK False-Positive Fix Review (0.7.209)
  38. Jun 4Hub/Spoke Attack Surface — Resolution
  39. May 31PUSH IT Hub-Controls Attack Surface
  40. May 26Deep Review: 0.7.184
  41. May 23Hub-Initiated Maintenance Mode
  42. May 22SMACKBACK File-Integrity Monitoring
  43. May 22Maintenance-Mode Session & Parse Error
  44. May 192FA Cookie Flags & Recovery Policy
  45. May 19Orphaned login.php at a Predictable URL
  46. May 19Installer Admin-Creation Bypass
  47. May 18Installer Step-5 CSRF Bypass
  48. May 18Post-Remediation Verification
  49. May 18Delta Review: 0.7.147–0.7.152
  50. May 10Multisite Remote-Admin Surface

llms.txt — When Descriptive Data Looks Like Instructions

llms.txt does not execute code. The risk starts when an outside AI agent reads machine-oriented documentation, mistakes untrusted text for instructions, and has permission to run commands. SnapSmack’s generated file contained no installers, packages, fetch-and-run commands, or llms-full.txt, so this audit did not uncover a live command payload. It did find the boundary worth fixing before somebody had a reason to abuse it: the site name, description, and configured URL were inserted directly into agent-readable Markdown. A poisoned setting could manufacture headings, arbitrary links, role-labelled prompt text, or command-shaped prose for a badly sandboxed agent to follow.

Release 0.7.575 makes those fields inert. Names and descriptions become single-line, length-bounded descriptive text; Markdown controls and publisher links are removed; descriptions containing prompt overrides, role injection, install commands, command substitution, or fetch-and-run syntax are omitted. Site URLs must be valid HTTP or HTTPS addresses with no credentials or control characters. Generated links are limited to the current site and hard-coded SnapSmack attribution, and SnapSmack continues not to publish the much larger llms-full.txt surface. Twenty-one regression checks pin the boundary. The detailed report also corrects an initially over-broad proposal: SMACKBACK protects the shipped generator, while each site’s generated output is compared with that site’s expected content rather than one impossible universal hash.

Read the full report →

Multisite Federation — Cross-Blog Trust, the Public Directory & SNAP SLAPPER

SnapSmack blogs can now connect into a hub-and-spoke network — one blog sends commands to the others — and photoblogs.fyi runs a public directory blogs can opt into. Both are exactly the kind of new machinery worth attacking, so they got a dedicated review: four reviewers reading the shipped code in parallel, every finding re-checked by hand. The engine itself — the cryptographic signatures on federated messages, the outbound requests, the remote-job runner — held up. Two serious issues were in the access-control layer around it. First, because a hub and a spoke share one secret key, and four hub commands weren’t checking that the caller really was the hub, a spoke — or an attacker who had compromised one — could turn its own key around and, on the hub, mint a posting credential, read the email address and IP of everyone with a pending comment, add posts, or rewrite the blogroll. All four now require the caller to be the hub, the same check every other command already carried.

Second, the public directory sign-up had no login and trusted whatever was posted to it, so anyone could overwrite an approved listing with their own content — and it stayed live — or delist any blog by address. The hub now verifies every request by calling the site back and reading the listing straight from the site itself, so a forged submission can only make the hub re-read that blog’s own truth: it cannot inject content, and it cannot remove a site that still lists itself. New members still wait for human review. Three more were closed alongside: a hub-to-spoke image fetch that could be pointed at the private network, a photo-editor build (SNAP SLAPPER) that shipped the fleet’s credential code it never runs — no secret was ever embedded in it — and two minor items, a directory tracking-beacon trick and a one-in-a-million join-key race. Closed in 0.7.559, and published here only after the fix was live on every blog — because a working description of an unpatched hole is the opposite of protecting the people running these sites.

Read the full report →

CMS Architecture Assessment — Direct-Access Guards & Content-Model Integrity

Where 047 and 048 reviewed the code line by line and against a live target, 050 was a structural review of the whole CMS against established publishing, media-library, database, recovery, security and federation patterns. It closed one security finding. Sensitive server-side includes — updater.php, release-pubkey.php (the release-signing public key), skin-registry.php, manifest-inventory.php and the real authentication include auth-smack.php — carried no PHP execution guard of their own; only smackback.php did. They relied on a single Apache <FilesMatch> deny-by-name list, which evaporates on nginx or any host ignoring .htaccess, leaving each include reachable as its own URL. The list had also drifted: it named dead login/auth entries and omitted auth-smack.php. Severity was medium (a trust-boundary/defense-in-depth gap, not RCE and not a direct leak — db.php does not echo). Release 0.7.544 adds a server-agnostic 403 direct-access guard to each include and reconciles the <FilesMatch> list to the files that actually ship. The code is released; per-installation verification via the updater/repair path is recorded as pending.

A long-open data-integrity check for orphaned community records (comments, likes, reactions with no valid parent) was verified against the highest-risk site’s database: zero true orphans (comments 3819→0, likes 4→0), 9,975 images reconciled against 9,975 posts. It is disclosed with the mistake made during it: an earlier diagnostic used a posts-only definition of “orphan”, which is wrong because engagement keys to the image identifier by design; that assumption mis-flagged ~1,904 valid comments, which were briefly removed and then restored from backup before the corrected, display-aware check ran clean.

The review’s foundational finding is architectural, and an earlier audit missed it while reporting the code clean: the CMS had no single enforced definition of a published item — legacy solo publishing stored a photograph as a bare image row rather than a post, so publication state, ownership, dates, engagement, collections and federation identity had no one home. That is a data-model question a code review does not surface; its remediation (post-model inventory and transactional conversion, 0.7.534D onward) is in progress, not finished. Of eight findings dispositioned, the two closed here are disclosed; the remainder — including further hardening of administrator authorization and URL routing — are deferred with owners and deliberately not mapped in public until they are fixed and can be disclosed the same way. The review process itself was corrected, not just the finding: our internal compliance standard now requires a comparative review to produce a cited platform-by-platform matrix and a deviation register — it cannot report “clean” from internal knowledge alone — and adds explicit data-model invariants, so a missing post object or an unbenchmarked architecture is a gated check a future review must clear.

Read the full report →

CMS Compliance, Image Handling & the Missing Content Invariant

SECAUDIT 049 began as a conventional CMS and image-handling review. It confirmed strong controls around database parameters, escaping, security tokens, authentication, signed updates, upload naming, raster validation, and derivative generation. It also found real hardening gaps: public error disclosure, inconsistent live security-header delivery, no universal pre-decode pixel budget, uneven upload-directory execution guards, and no safe policy for administrator-uploaded SVG branding. Current source closes the code findings: visitor errors are generic, upload directories share one execution guard, local images have a pre-decode 4K ceiling, and branding SVG is strictly sanitised or rejected. Deployment and active-fleet verification remain recorded rather than being quietly marked complete.

The more important finding was architectural and should have been caught sooner. SnapSmack had no single enforced definition of a publishable content unit: legacy solo publishing could treat a photograph as both media and the post itself, while newer group and longform features used a separate post record. That allowed publication state, dates, ownership, engagement identifiers, collections, and federation identity to acquire competing meanings. Releases 0.7.534D through 0.7.536D added inventory and transactional conversion tooling, but the first diagnostic then made the opposite mistake: it treated valid images selected in a draft longform post bucket as old-style posts. Current source reports bucketed and unassigned longform media separately and prohibits photo-to-post conversion outside SMACKONEOUT. The repair is not being misrepresented as proof that the architecture is finished.

The first draft also wrongly described the intentionally dormant stable track as a fleet-delivery failure. No production installation runs on that track, so the claim was withdrawn. The legitimate delivery question is whether every active development/live installation receives and demonstrates the required controls. The corrected scorecard, the architectural limitation, the remaining verification work, and the limits of this review are all preserved in the report. SECAUDIT 050 is the dedicated comparative assessment of the complete CMS architecture against established publishing, media-library, database, security, recovery, and federation patterns.

Read the full report →

Live Penetration Test — Installer, Bootstrap, JSON-LD & Editor Boundary

SnapSmack’s first live penetration test attacked a disposable 0.7.527D installation as both an unauthenticated visitor and a compromised content-only editor. It confirmed two critical installation-state failures, one high-severity JSON-LD script-injection flaw, and one medium-severity editor-boundary gap. The test also exercised the major controls added after SECAUDIT 047 and confirmed that upload restrictions, signed downloads, sensitive-file blocking, traversal protection, session handling, login behaviour, reset throttling, and destructive-action security tokens held up.

The critical installer and bootstrap exposures, the script-injection path, and the missing editor restrictions were corrected in 0.7.528D and verified on the upgraded live site. Two additional LOW findings were also closed there. The report transparently records that HSTS and Content-Security-Policy did not reach upgraded installations through the original protected-file delivery path; their shipping correction is tracked separately and remains subject to live response-header verification.

Read the full report →

Full Multi-Dimension Audit — Authentication, Recovery, Federation & Uploads

Nine focused reviewers examined the live 0.7.525 development code across authentication, authorization, SQL injection, path handling, uploads and code execution, federation and server-side requests, cross-site request forgery, secrets and updates, and cross-site scripting. The review found four critical, seven high, three medium, and nine low issues. The most serious included editor accounts reaching administrator powers, recovery kits writing executable files, federated actors claiming another server’s identity, media uploads accepting executable content, and destructive admin actions reachable without a security token.

The remediation established a central administrator boundary, confined recovery imports, bound federation signatures and comment changes to their real actors, protected mutation links with security tokens, restricted uploads to verified images, generated unique per-site download secrets, hardened the Hub login, and closed the identified script-injection and server-side request paths. An adversarial follow-up found four sibling and race-condition gaps; those were closed as well. The narrow updater fail-open edge case remains documented as a deferred low-risk item because changing the fleet’s sole repair path requires a separate deliberate review.

Read the full report →

Audit Process Correction — Stale-Branch Simulation & Clean Release Delta

Claude was asked to perform a live penetration test. What it initially delivered was a simulated multi-agent source review, not attacks against a running installation, and the review targeted code roughly 400 versions behind the release under examination. That output could not support claims about the live product. Its findings were invalidated, its stale patches were not released, and its hypotheses were retained only as a checklist for a proper review against current code.

A separate line-by-line review of the real 0.7.524D-to-0.7.525D shipping delta remained valid and found no security defects. The broad current-code review was rerun as SECAUDIT 047, and the genuine live penetration test followed as SECAUDIT 048. We are publishing 046 because mistakes in the audit process belong in the security record too; removing the number or quietly relabelling the simulation would be less honest than showing what failed and how it was corrected.

Read the full process record →

Pixelix Live Test & Release Lifecycle

The real-device contract now works: Pixelix registered, passed through the normal password and two-factor login, received owner consent, staged a photograph as a private draft, updated its ALT text, published it, refreshed the profile, and loaded the resulting thumbnail. The final compatibility patch also stopped optional settings, account, city-search and collections calls from falling through to HTML errors.

The successful post did not make the first pass clean. It found five follow-up items: abandoned media and OAuth records had no expiry cleanup; a read token could request an unpublished post by numeric ID; bearer rows with a missing access-token deadline failed open; code redemption and refresh rotation were not atomic under concurrency; and the same public 520D tag had been moved repeatedly after deployment.

All five are closed. A bounded seven-day draft-media lifecycle removes abandoned files and OAuth residue safely; client status reads are published-only; null-expiry bearers are rejected and legacy rows revoked; token redemption and rotation use locked, conditional single-winner updates; and Smack Central now burns every release identifier into a permanent ledger before publication while recording its source commit, checksum and signature. The 520D history cannot be made immutable retroactively, but that identifier is retired and the packaging path now refuses reuse.

Read the full report →

THE HUB — Shared Profiles, Prompts, and the Launcher Boundary

The shared profile and Gemini-prompt stores held up: filenames are confined, writes are atomic and recoverable, migrations are repeatable, and no new network or shell surface was introduced. An independent adversarial pass did, however, overturn the first review’s low-risk rating on the launcher. THE HUB searched for wildcard-matched programs inside the same folder tree GYSS is allowed to write. A compromised GYSS window could plant a program there and make it appear installed. Hub 0.1.2 removes and defensively rejects those in-jail wildcard searches.

The follow-up closes the architectural remainder too. GYSS’s privileged file commands can now reach only shared_library and its own config_files\gyss data. The sibling SYBU and COLD SNAP executable directories are outside that allowlist, including when addressed by exact path. A Rust regression test pins the legitimate data paths and rejects executable, lookalike-directory and traversal paths.

Read the full report →

Pixelix Posting & OAuth — GRAMOFSMACK Only

GRAMOFSMACK can now accept photo and carousel posts from Pixelix through a deliberately narrow Pixelfed-compatible API. The connection uses owner-approved OAuth, expiring access and refresh credentials, explicit read/write scopes, administrative disconnect, and the existing owner switch for offline posting. Other SnapSmack install modes fail closed.

The first security pass found six issues before release: one authorized client could claim another client’s staged media; public client registration lacked a throttle; public discovery attempted database schema changes; concurrent uploads could race the hourly posting cap; refresh credentials had no hard lifetime; and recorded scopes were not enforced. All six are remediated. Media is bound to its originating OAuth connection, registration and posting limits are serialized, schema work stays in the updater, refresh credentials expire after 90 days, and every bearer route checks its required scope. Targeted syntax and regression gates pass; a real Pixelix device and live-database concurrency run remain rollout checks.

Read the full report →

Desktop Tools — What “Already Fixed” Was Hiding

Three days earlier we fixed a problem where a desktop tool could send your password or your access key across the internet unencrypted, and we wrote that the fix covered five tools at once. The fix was real. The claim that it covered five tools was not. The repair lived in a shared piece of code, and a shared safety check only protects the tools that actually call it — three of them never did. Nothing in the code could tell the difference between “protected” and “never connected up”, so the record said everything was fine while three tools carried on exactly as before.

Unzucker, the Instagram importer, sent its access key in the clear on every request if the blog address began with http instead of https. Smack Up Your Backup was worse: it sent your actual admin password that way, from two separate places — one of which was the exact file an audit had flagged a week before and a later audit had marked as fixed. Smack Your Batch Up sent its key unchecked from three different screens. All are now checked before anything is sent. The password paths refuse outright, because a key can be cancelled and reissued but your password cannot be without locking you out of your own site. The key paths warn clearly and ask you to confirm. Connecting to your own computer for testing is unaffected.

Two smaller things in Unzucker were fixed at the same time. If your computer's password store was unavailable — locked, or missing on that system — saving your settings quietly threw the access key away instead of falling back to storing it, so the tool came up blank next time with no explanation. And its settings file, which holds that key when no password store exists, was readable by any other account on the machine; it is now owner-only, as the equivalent file in FLKR FCKR already was.

The lasting change is in how this is tested. The new checks do not just confirm the safety code works — they confirm each tool actually calls it, before the credential is sent, in every place a connection is made. Tests that only checked the safety code itself would have passed happily throughout the entire period these tools were exposed. One tool, Oh Snap, has not been examined yet and is scheduled next. No exploitation is known; these are single-operator tools and an attacker would need to be positioned on the network between you and your own server.

Read the full report →

Download Links — Escaping Is Not Validation

SnapSmack lets you attach a download link to a photograph, pointing at a full-resolution copy on Google Drive, OneDrive or anywhere else. That link can be set by the desktop posting tools over their scoped key. Nothing checked what kind of link it was. A link does not have to point at a file — it can be written so that clicking it runs code in the visitor's browser instead, and that code runs as though it came from your site.

What makes this one worth reading is why it survived review. The page that draws the download button did pass the link through an escaping function, and escaping is a genuine defence — it stops a value breaking out of the surrounding HTML. But it works by neutralising particular characters, and this kind of malicious link contains none of them. The code looked defended, was defended against a different problem, and read as correct. Elsewhere, the floating social dock re-read the same link and printed it with no protection at all.

All four places — the two that store the link and the two that display it — now require it to be an ordinary web address before it is used. The two that display it re-check rather than trusting what is already saved, because links stored before this release are still in the database. If a stored link fails the check, the button quietly falls back to SnapSmack's own internal download instead of disappearing.

This was found while reviewing the IndieWeb markup added the same day, which turned out to be sound — and which had already closed a similar gap on the social dock's profile links on its way past. No exploitation is known: it needs a bad link to have been stored in the first place, and a visitor to click the download button.

Read the full report →

Smack Up Your Backup’s First Review — Closed at Last

The backup tool’s first security review was done back in April. Its findings were fixed over the following months, but the report itself was never updated and never posted here — so the earliest audit of the tool that holds your server passwords has been the one thing missing from this page. This closes it, with every original finding re-checked against the code as it stands today rather than taken on trust from a later report.

The one that reads alarmingly in the original is a Google service-account key — the credential that lets the tool upload to your Drive — sitting loose in the tool’s folder, where the ignore rules would not have stopped it being committed. The April report said, correctly, that if such a key has ever been committed it must be treated as public and rotated immediately. It never was. Checking the entire history of the repository, on every branch, turns up no commit of that file at any point. The risk was real — it was one careless command away from being published — but the key was never exposed, no rotation was ever needed, and the recommended ignore rule was adopted word for word.

Of the other four: passwords stored with reversible encoding are now in the passphrase-locked vault that arrived with the August review of the same tool. An unescaped filter that could break a Drive query is escaped. A debug log that quietly recorded activity to a file beside the program on every machine is gone entirely.

One is not fixed, deliberately, and this page would be worth less if it did not say so. FTPS certificate checking is still off by default. Most photographers running this are on inexpensive hosting that ships self-signed or expired certificates, and switching validation on would break the majority of real setups on the first connection — trading one bad outcome for another. The answer is not to demand a perfect certificate; it is to remember the certificate your server presented the first time and warn you if it ever changes, which is exactly what the tool already does for SFTP. That work is scheduled. Until then it is recorded here as an open decision with a date on it, not quietly dropped.

Read the closure record →

FLKR FCKR — Credentials at Rest, in Transit, and Trusted Paths

FLKR FCKR moves a Flickr export into a photo blog. Worth saying plainly, because the name suggests otherwise: it holds no Flickr credentials at all — it reads the export folder Flickr gave you, off your own disk. What it does hold is the key to your blog. Four findings, all closed.

The one that mattered was not in this tool alone. When an action needs more than the tool's ordinary permissions, it asks for your account password and a two-factor code and sends them to your blog. That shared piece of code never checked whether the address was encrypted. If a blog address had been entered as http rather than https, your password and a live two-factor code crossed the network in the clear, and a code stays usable for the rest of its short window. It now refuses outright — not a warning, a refusal — because unlike an access key, a password cannot simply be cancelled and reissued. Connecting to your own machine for testing is unaffected.

Second, the tool's access key was stored on disk with encoding rather than encryption — reversible by anyone who had the folder. It now uses the same passphrase-locked vault built for Smack Up Your Backup: your passphrase is never written anywhere, so a copy of the folder is no longer enough. Lose the passphrase and nothing is destroyed; paste the key again or issue a new one. The tool must be rebuilt for this to reach you.

Two server-side items closed alongside. The blog stored image file paths exactly as the tool sent them, and those same paths are handed to the delete function later when you remove a photograph — so an unchecked value was a delayed, arbitrary file deletion waiting for an ordinary action to trigger it. Paths must now name a file the upload endpoint itself wrote. And an imported comment's author link was stored without checking it was a real web address, which mattered because that link is later rendered as a clickable link. Both now validated on the way in.

One thing deliberately not changed: imported photographs keep their EXIF data, including GPS coordinates, exactly as Flickr held it. An importer's job is to move your archive, not to quietly edit it. That is a settled decision, recorded in the report as such, and not a finding. No exploitation is known; the remaining findings require either local access to your own computer or a valid key and an open authorisation window — which in normal operation means you.

Read the full report →

GET YOUR SHIT SORTED — Desktop Trust Boundary & API Key Lifetime

GET YOUR SHIT SORTED is the desktop sorting tool. Its window displays information fetched from the blog it is connected to, and it also had three unrestricted commands for reading and writing files on the computer running it. Those commands accepted any location on disk, the window had no content-security policy, and a handful of values arriving from the server were displayed without being neutralised first. Individually each was minor. Together they formed a path: a blog that had been tampered with — or an unencrypted connection someone else was sitting on — could have caused the tool to write a file somewhere it should never write. No exploitation is known, and the tool is used by one operator on their own machine.

The file commands are now confined to the tool's own data folder and reject any attempt to climb out of it. Every value arriving from the server is neutralised before display. Connecting over an unencrypted address now warns plainly that the key travels in the clear, and asks for a deliberate confirmation. Separately, the tool's API key was never checked for expiry — keys are issued with a lifetime of four weeks or less, but this handler honoured them forever. Reviewing that led to a sweep of every key-checking entry point in SnapSmack: three more had the same gap, and one accepted a key issued for a completely different tool. All four now verify both the key's type and its expiry.

Two items remain open by decision rather than oversight. The tool still stores its key with encoding rather than encryption, which will be replaced by the same protected vault that closed the equivalent finding for Smack Up Your Backup. A content-security policy is prepared but needs testing against a running build before it ships. AI enrichment, which spends money with a third-party provider, now states the number of paid requests before a run begins and counts them as it goes; it remains unavailable unless AI has been deliberately enabled and cost accepted on the blog itself. Closed in 0.7.505D.

Read the full report →

Gallery Skins — JavaScript Package Boundary

SnapSmack skins choose the site's look, while reusable browser behaviour belongs to a shared, reviewed engine library. The code followed that design most of the time, but the Gallery packager did not enforce it: several official skin folders still carried their own JavaScript, and a future package could have included a script, an inline event handler, or a remote script reference and still reached the signing step. The files we found were legitimate features, not malicious code. They exposed a missing rule at the publishing boundary.

That rule is now absolute: a Gallery skin ships no JavaScript. The official skin-local copies have been removed or moved into SnapSmack's shared engine library, where one repair reaches every skin that uses it. A repository scanner checks for bundled files, inline scripts, event handlers, JavaScript links, remote scripts, and active embedded content. Smack Central runs that same check before it creates, signs, or publishes a package; any blocking finding stops the build. A signature now answers who produced the package, while the clean gate separately proves that the package obeys the no-JavaScript policy.

The complete official skin set scans clean, the moved engines are registered in the core inventory, and the packager fails closed. No exploitation is known. Closed in 0.7.500D.

Read the full report →

Smack Up Your Backup Desktop — Credential Vault & Transport

Smack Up Your Backup is the desktop app that copies your whole site — images, database, everything — down to your own machine and off to cloud storage. To do that job it has to hold real keys: your FTP password, your site admin password, scoped API keys, and your Google Drive and Box tokens. This audit looked at how it kept them, and how it talked to your server.

The finding that mattered: those keys were sitting on disk with no real protection — plain text or trivially-reversible base64. Anyone who got hold of the backup folder (a lost laptop, a synced Dropbox, a shared PC) had working credentials to your site and your cloud backups. It now keeps every secret in a passphrase-locked vault (scrypt key derivation with Fernet encryption); lock it and the secrets are gone from memory, and switching it on leaves no plaintext copy behind. If the vault is locked or a write fails, it stops rather than quietly falling back to the old plaintext — it fails closed. Two smaller items closed alongside: the restore path can no longer be tricked by a doctored backup manifest into writing outside its target folder, and SFTP now remembers your server's identity on the first connection and warns you if it ever changes, which catches a machine-in-the-middle without needing anything from your host.

One thing was left as-is on purpose: for plain FTPS, certificate checking stays off by default, because the budget shared hosts most photographers use ship broken or self-signed certificates and turning it on would simply break everyone's backups. That tradeoff is documented in the report, along with the planned fix — pinning your host's certificate fingerprint on first use, the same trick now used for SFTP, so you get tamper detection without needing your host to fix their certificate. Closed in SUYB 0.7.19.

Read the full report →

SmackPress WordPress Migration — Import Sanitising

SmackPress is the tool that moves an existing WordPress blog into SnapSmack. This audit walked its whole path — the authenticated import API, the image upload, and the desktop client — before the first real blog was migrated. The core held up well: every database query is parameterised, the import key is a short-lived 256-bit token stored only as a hash, and uploaded images are re-encoded so they can't smuggle in code.

The finding that mattered: a WordPress post body is raw HTML, and plugins inject all manner of things into it — including scripts. SmackPress was storing that HTML as-is, so anything hostile in a source post could have run on the new site for every visitor. It now passes every imported post and page through a strict allowlist that strips scripts, styles, iframes, event handlers, and javascript: links before anything is saved — verified against a dozen attack payloads. Two smaller items closed alongside: GIF uploads are now re-encoded like every other image format, and the desktop tool keeps your WordPress and API keys in your operating system's keychain instead of a plain file. Closed in 0.7.496.

Read the full report →

IP SMACKER & Login Shield — Client-Address Spoofing

A fleet check found impossible addresses in every site's shared ban table, including loopback and private-network addresses that could only belong to our own infrastructure. Tracing those records exposed a critical trust mistake: IP SMACKER and the login brute-force shield accepted forwarded-address headers without first proving the request had actually arrived through a trusted proxy. A scanner could therefore choose who was banned, rotate its claimed address to avoid the five-strike login limit, or deliberately lock out a victim.

SnapSmack 0.7.451 closed the remotely exploitable spoofing path; 0.7.453 completed the shipped-code remediation. All four known ban writers and the shared-address rate limiters now use the mandatory trusted resolver and independently refuse infrastructure addresses as ban targets. Trusted proxies accept validated IP or CIDR entries, Configuration shows the observed peer and selected client, and permanent regression tests cover direct, tunnel, forged-header, and malformed-address cases. BREAK THE GLASS also preserves both the selected operator address and the observed network peer.

The report is CLOSED in 0.7.454. The trusted-address fix remains in place, and the remaining operational work is now complete: the backed-up update performs a one-time reset of potentially forged automatic rows, duplicate bans keep a fixed expiry, expired and unsafe rows are pruned, table growth is bounded, and administrator-login bans trigger an out-of-band owner warning.

Read the full report →

Skin Manifest RCE & Credentials Export — Closure

An early design decision stored skin metadata as executable PHP. A simple question — what happens if a submitted skin puts code in its manifest? — exposed the unnecessary trust boundary: loading the metadata could execute arbitrary code with the web server's privileges. There is no evidence it was exploited. Every skin now uses strictly parsed JSON metadata, official packages require signatures, unsafe ZIP paths are rejected, and the temporary transition bridge is gone now that the whole fleet has migrated.

The same review removed a redundant USER CREDENTIALS download that exported the complete user table even though the protected Recovery Kit already contained it. A closure pass found the credential-only mode still available through the authenticated SUYB export endpoint, so that second route and the shared engine capability were removed too. Recovery remains intact; the extra attack surface does not.

Read the full report →

Fediverse Client — Attack Surface

SnapSmack is a fully integrated, Pixelfed-compatible single-user server — your blog's own home on the Fediverse, speaking the ActivityPub protocol so it can follow, like, comment on, boost, and message people right across the network, and be followed back. Because it is fully interactive, it shows content written by people on other servers, so this audit walked that entire trust boundary. The engine room held up well: requests arriving from other servers are cryptographically verified before they are allowed to change anything, nobody can pose as someone they aren't, and the software is fenced off from reaching back into your own network.

Two medium findings were in the browser display code, where a hostile profile or post could have slipped a booby-trapped link into a page you were viewing. Both are closed — links coming from other servers are now checked to be ordinary web links before they are shown, and the profile-bio display was rebuilt to permit only safe formatting. A low-risk hardening item on an internal search request was tightened for good measure, and two informational notes were reviewed and accepted. Closed in 0.7.405.

Read the full report →

Fediverse Search — Token Isolation

SnapSmack can borrow a login token you hold on a friendly Fediverse instance so your blog can run authenticated searches across the network. This review checked how that token is stored and used. The one finding: the key protecting the stored token was falling back to a shared default value instead of a per-site secret. Fixed — every install now generates its own dedicated, random search key, so no two sites share protection.

A second item flagged around form security turned out to be a false alarm: every admin form on SnapSmack already carries automatic cross-site-request protection. Everything else passed — the outbound requests are guarded against server-side request forgery, results render without script injection, adding an account is gated behind password + two-factor, and the token is never exposed to the browser. Closed in 0.7.376.

Read the full report →

Cloud Backup From the Web Host — Feature Removed

SnapSmack used to offer a Cloud Backup feature that pushed your backups from the web server straight to your Google Drive or OneDrive. To do that unattended — on a schedule, without you present — it had to keep a long-lived access token and the associated secret on the web host, in the site's settings table.

That is a poor place for them. Most photographers running this are on inexpensive shared hosting, where you do not control who else is on the machine. A server compromise, a leaked database dump, a backup folder left readable — any of those hands someone a working token for your cloud storage, and the token keeps working until it is revoked.

This was not patched. It was removed, in 0.7.324. The right answer was not to guard the credential better but to stop the web host holding it at all. Pushing backups to the cloud now happens from Smack Up Your Backup on your own computer, where the credential lives with you rather than on a rented machine. The Backup page says so plainly where the feature used to be, rather than quietly dropping it.

Publishing this one late: the fix shipped in June and the write-up was never put on this page. The record should be complete whether or not anyone was watching.

Read the full report →

Skin Manifests Could Run Code — The Finding

Every skin shipped a settings file that SnapSmack did not read as data — it executed it, as program code, on public pages and inside the admin panel. That means any skin present on the site could run whatever it liked with the site's own permissions: read or alter the database, take secrets, write files, change settings, or quietly rewrite what the admin screens showed you.

The informal worry at the time was “a bad skin could change some options”. That understated it considerably. It was full remote code execution, limited only by what the web server itself could do — and skins are exactly the kind of thing people are encouraged to download and try.

Remediated in 0.7.440. Skin settings are now read as data and cannot execute. Because the fix changed what a valid skin package looks like, deployment was deliberately gated on rebuilding and re-signing every hosted skin, so no site was left able to install an old-format package.

This report is the finding, and it is being published now that it has long been closed — while it was open, publishing a working description of how to run code on someone's photo blog would have been reckless. The closure record went up in July as Skin Manifest RCE & Credentials Export — Closure; this is the other half of the story.

Read the full report →

Cross-Site Request Forgery — Closed Site-Wide

Cross-Site Request Forgery (CSRF) is a trick where a booby-trapped web page quietly gets your browser to fire a real action on a site you're already logged into — without you clicking anything that means to. The defence is a per-session token that proves a request genuinely came from your own admin screens and not from somewhere else.

Across our early-2026 review series, site-wide CSRF protection was the one item we flagged and deliberately deferred while higher-severity findings were closed first. It's now done: every admin form and every background request in SnapSmack carries and checks that token automatically, with no per-page switch anyone can forget to flip. That closes the single open thread carried through reports 001 to 008.

Read the closure record →

Underlying reviews: 001, 002, 003, 005, 006, 007, 008.

Son of a Batch — Batch Poster Review

A security review of the batch-posting pipeline that pushes large image sets to your site. Findings fixed or mitigated; desktop-side encryption of stored keys at rest is noted as a tracked follow-up rather than a live exposure.

Read the full report →

Dev-File Leak & SMACKBACK Blind Spot

Closed a path where development-only files could be swept into a release package, plus a SMACKBACK blind spot around the release-staging directory. The integrity monitor now flags leaked central code if it ever lands on a normal install. Closed in 0.7.317.

Read the full report →

Auto File-Deletion Attack Surface

A pre-emptive review of the attack surface around automatic file deletion. Confirmed the dangerous capability was never actually shipped; the review closed with no exploitable exposure on any live site.

Read the full report →

SMACKBACK Unknown-File & Cron-Verify Gap

Two SMACKBACK gaps — how it handles unexpected files, and a timing gap in the scheduled verification pass. Both addressed; the only residual was a low-sensitivity information item handled operationally.

Read the full report →

Installer Constants & Skin Attack Surface

An installer self-breach around how configuration constants were written, alongside a review of the skin attack surface. Remediated, with the skin-side hardening folded into the manifest-only JavaScript policy.

Read the full report →

Skin Inline-Script Manifest Bypass

Skins could ship an inline script tag that slipped past the manifest-only JavaScript policy — the rule that keeps every install's scripts reviewed and accounted for. The last remaining carrier, Photogram's landing feed, was moved to a manifest-loaded engine file. Closed in 0.7.317.

Read the full report →

Mesh Roster Key Broadcast

A review of the multisite mesh-roster key broadcast. The UI key exposure was fixed — keys now show once, then are hidden — and encryption of those keys at rest is a documented, accepted residual rather than a live hole.

Read the full report →

Imported-Caption XSS — Remediation

The remediation record for a cross-site-scripting risk in captions brought in during import — confirming the fix landed and imported caption text is properly sanitized before it is ever displayed.

Read the full report →

SYBU Recovery & Unzucker Captions

A review of the backup-recovery flows and Unzucker's caption handling. Findings resolved; the caption cross-site-scripting item is closed out in its own remediation addendum (024A above).

Read the full report →

Unzucker Attack Surface

A full review of the Unzucker desktop importer's attack surface. The transport-layer findings are now moot: Unzucker moved to HTTPS with Bearer-token auth, removing the old FTP and cross-site-request surfaces entirely.

Read the full report →

Canonical-Schema Fetch Hardening

Closed a gap where a remote database schema could be applied without verifying its signature first. The fetch path now checks the signature before anything touches your database. Fixed in 0.7.214, with no residual open items.

Read the full report →

SMACKBACK False-Positive Fix Review (0.7.209)

A review tied to the 0.7.209 fix for SMACKBACK false-positive breach alerts. The release deliverables shipped clean; two follow-ups were flagged at the time, and the one that mattered for public installs — denying direct web access to the integrity manifest — is closed in 0.7.317.

Read the full report →

Hub/Spoke Attack Surface — Resolution

The closing record for a deep review of the multisite hub-and-spoke attack surface — the machinery that lets one install manage a fleet of others. Every finding resolved, the bulk of them in 0.7.203.

Read the full report →

Underlying review: 021.

PUSH IT Hub-Controls Attack Surface

A review of the PUSH IT hub controls — the fleet-wide action buttons that let a hub act on every spoke at once. Findings closed, with one accepted low-risk item documented.

Read the full report →

Deep Review: 0.7.184

A deep review of the large 0.7.184 feature drop — mandatory 2FA, self-sealing breach lockdown, the skin JS scanner, and more. All five findings closed: fixed, or confirmed safe by design.

Read the full report →

Hub-Initiated Maintenance Mode

A review of the new multisite feature that lets a hub put its spokes into maintenance mode. Closed with no security findings requiring remediation — it shipped clean in 0.7.171.

Read the full report →

SMACKBACK File-Integrity Monitoring

A design review of SMACKBACK, the file-tamper monitor that watches your install for unexpected changes. Items addressed in 0.7.170; no exploitable issues left open.

Read the full report →

Maintenance-Mode Session & Parse Error

Two minor defects in maintenance mode — a session-handling issue and a parse error. Both fixed in 0.7.169.

Read the full report →

2FA Cookie Flags & Recovery Policy

Tightened the security flags on the two-factor verification cookie and firmed up the recovery-code policy, so the second factor stays a real second factor. Fixed in 0.7.159.

Read the full report →

Orphaned login.php at a Predictable URL

A duplicate login page was sitting at a guessable path, quietly bypassing the configurable login-slug protection that's supposed to hide your front door from bots. Removed and fixed in 0.7.155.

Read the full report →

Installer Admin-Creation Bypass

A leftover installer could be used to create a brand-new admin account on an already-installed site, sidestepping two-factor auth entirely. Closed in 0.7.157.

Read the full report →

Installer Step-5 CSRF Bypass

A cross-site-request-forgery bypass in step 5 of the installer. Closed as part of the site-wide CSRF work recorded in the closure at the top of this page.

Read the full report →

Post-Remediation Verification

A second pass confirming the previous review's fixes were actually applied correctly — because "we fixed it" should be something you can check. All seven items verified resolved or confirmed clean.

Read the full report →

Delta Review: 0.7.147–0.7.152

A routine review of everything that changed across half a dozen releases. Two low-severity housekeeping items found and resolved; no exploitable vulnerabilities.

Read the full report →

Multisite Remote-Admin Surface

A full review of the new hub-and-spoke remote-admin features before they went out. All five findings were remediated in 0.7.102 ahead of release — nothing reached a live site unfixed.

Read the full report →

Found Something? Tell Us.

SnapSmack is not bulletproof. No software is, and anyone who tells you theirs is, is lying. If you've found a security issue, we want to know about it — and we'd rather you tell us quietly than tell the internet.

Report it privately through the SnapSmack support forum rather than posting details in public, and give us a chance to close it before it's common knowledge. That's the same courtesy we extend to you: we don't publish the details of a serious, open issue until it's fixed. Once it's closed, it joins the list above. The codebase is public and open to inspection at any time — if you find something we missed, that's a contribution, and it's welcome.