Moscow UTC+3
Typographic cover on black: “8 found. 8 fixed.” above four findings from the tarasovvitalii.com audit, each with a severity label and a lime “Fixed” badge.

A white-box security review of this portfolio: its chat, message API, server and 15 concept demos. We found 8 issues, including script injection on the chat’s origin, and fixed all 8.

Labels
Internal VITON13's own product
Year
2026
Disciplines
Security, Websites
Languages
English, Russian, Spanish, Chinese

Overview

This site is more than static pages. It runs a chat on VITON ID (Firebase Auth and Firestore), a small Node API that stores contact messages and sends email, an nginx server in Docker, and copies of 15 concept sites under /demos/. On 28 September 2026 we reviewed all of it from the inside: the code, the Firestore rules, the server and container configuration and the npm dependencies.

For each part we asked an attacker’s question: what can someone outside do with it, and what would it cost the owner? A finding counted only once we had reproduced it, and every fix was re-tested on a local copy of the production setup before release.

  • 8

    security findings, all fixed

    Source: Code review, 28 Sep 2026

  • 1

    high-risk issue: script injection on the chat’s origin, now closed

    Source: Reproduced before and after the fix on a local copy

  • 119

    demo pages loaded under the new security policy with 0 violations

    Source: Headless Chrome check, 28 Sep 2026

  • 0

    known vulnerabilities in the site and API npm dependencies

    Source: npm audit, 28 Sep 2026

Approach

We read every API route, the token verification, the Firestore rules with their 39 emulator tests, the nginx and Docker Compose files and the code of all 15 demos. Instead of probing the live server, we ran the same containers locally (nginx and the API with production settings) and tried each attack there.

The most serious finding was in a demo, not in the site itself. The Oriva catalogue put the ?cat= address parameter into the page as HTML, so a crafted link ran script on tarasovvitalii.com, the origin where the chat keeps its Firebase session. One click by the owner could have handed over the inbox. The other 14 demos only compare address parameters with known values.

Fixes: the demo accepts only known categories and sort orders; /demos/ has its own Content-Security-Policy; Leaflet is pinned with integrity hashes; the /demos/<id> redirect is rebuilt from a strict pattern; addresses that could smuggle fields into mailto: links are refused; a global daily cap protects storage; the API runs as an unprivileged user on a read-only filesystem; the nginx version is hidden and HSTS covers www.

Results

8 findings, 8 fixed: 1 high, 1 medium and 6 low. The payload that ran on the old demo code does nothing on the new one, and the request that used to inject a Set-Cookie header now gets a plain 404. All 119 demo pages load under the new policy with zero violations, the API test suite passes 48 of 48 with the new rules covered, and npm audit reports 0 known vulnerabilities in the site and API dependencies.

Checked and found solid: Firebase ID token verification (RS256 only, key id required, audience, issuer and expiry checked), the Firestore rules (no one can read another person’s thread or write as the owner), the email templates (every value escaped, no header injection), the logs (no tokens, emails or IP addresses) and the chat UI (visitor text is never inserted as HTML).

Security review

Every finding below was reproduced before it counted and re-tested after the fix, on a local copy of the production containers. Ordered by severity.

Reviewed 28 Sep 2026 · findings: 8 · fixed: 8

  1. High

    Script injection in a concept demo, on the chat’s origin

    Fixed

    Before

    The Oriva catalogue inserted the ?cat= address parameter into the page as HTML. A crafted link ran script on tarasovvitalii.com, where the chat keeps its Firebase session: one click by the owner could expose the inbox and the owner account.

    After

    Only known category and sort values are accepted. The same payload no longer runs, and the other 14 demos were checked for the same pattern.

  2. Medium

    Demos without a security policy, third-party script without an integrity check

    Fixed

    Before

    52 demo pages loaded Leaflet from unpkg.com without an integrity hash, and /demos/ sent no Content-Security-Policy, so a compromised CDN or any injected script could reach everything on the origin.

    After

    Leaflet is pinned with SRI hashes and /demos/ has its own policy: requests only to the site itself, scripts only from the site and the pinned file, no plugins. 119 pages, 0 violations.

  3. Low

    Header injection in a server redirect

    Fixed

    Before

    The redirect from /demos/<id> to /demos/<id>/ echoed the decoded path back, so %0d%0a in a link wrote an extra response header, for example Set-Cookie.

    After

    The redirect accepts only letters, digits, hyphens and underscores and builds the address itself. The same request now gets a plain 404.

  4. Low

    Hidden fields in the owner’s “Reply” links

    Fixed

    Before

    An address like me@x.com?bcc=…&body=… passed validation, so the Reply link in the owner’s inbox and emails pre-filled a hidden BCC and text. Names could also carry direction-override characters that disguise text.

    After

    Addresses with URL delimiters are refused, addresses in mailto: links are encoded and direction-override characters are stripped. New API tests cover all three.

  5. Low

    No overall limit on stored messages

    Fixed

    Before

    Limits were per IP address only. Rotating addresses could keep adding contact-form and bot entries until the disk filled up.

    After

    A global daily cap on stored submissions (500 by default) sits on top of the per-IP limits. Beyond it the form answers “try again later”.

  6. Low

    Message API running as root

    Fixed

    Before

    The API container ran as root with a writable filesystem and all default Linux capabilities, so any bug in it would give an attacker a comfortable foothold.

    After

    It runs as the unprivileged node user on a read-only filesystem, with every capability dropped and privilege escalation disabled.

  7. Low

    Server version on display, outdated nginx branch

    Fixed

    Before

    Every response carried “server: nginx/1.27.5”, and the site container used the 1.27 branch, which no longer receives fixes.

    After

    The version is hidden on every response, and the container runs the current stable nginx line.

  8. Low

    HTTPS enforcement skipped www

    Fixed

    Before

    The HSTS header, which tells browsers to use only HTTPS, was sent on the main domain but not on www, and it did not cover subdomains.

    After

    www sends HSTS as well, and the policy now includes subdomains, so http://www… is upgraded too.

What this does not prove

This is our own site, not client work. The review was done by us from the source code; it is not a third-party penetration test or a certificate. We tested the attacks on a local copy of the production containers, not against the live server.

The result covers what we checked on 28 September 2026: new code, dependencies or demos need the same review again. The demos still share the site’s origin. The new policy limits what injected code could reach; moving the demos to their own domain would isolate them completely.

Planning something similar? Our founder, Tarasov Vitalii, reads every message himself.

All work

Next project

Möbius School & InstituteSchool & university website with a personal account Concept2026 All work (32)