upload_prescan report — 8 file(s), 18 finding(s)

HIGH   UPLOAD-EXEC-EXT-ALLOWED    hostile/gallery.php:3  executable extension in an allow/accept list — a script upload becomes server-side code
       | $allowed = array('jpg', 'png', 'php', 'svg', 'html');
HIGH   UPLOAD-CLIENT-MIME-TRUST   hostile/gallery.php:6  validation gate reads the client-supplied type field — attacker declares the type; check the bytes instead
       | if (in_array($_FILES['pic']['type'], $allowed)) {
HIGH   UPLOAD-CLIENT-MIME-TRUST   hostile/upload.js:11  validation gate reads the client-supplied type field — attacker declares the type; check the bytes instead
       | if (req.file.mimetype === 'image/png') {
MEDIUM UPLOAD-ORIGINAL-NAME-PATH  hostile/extonly.js:11  client filename inside the save/join path — traversal + overwrite + predictable landing
       | req.file.mv('/var/app/data/blobs/' + req.file.originalname);
MEDIUM UPLOAD-BLACKLIST-EXT       hostile/filter.py:2  exec-ext deny list — blacklist validation bypassable (case/double-ext/trailing-dot/alternate names)
       | BLOCKED = ['.php', '.exe', '.sh', '.jsp']
MEDIUM UPLOAD-ORIGINAL-NAME-PATH  hostile/filter.py:9  client filename inside the save/join path — traversal + overwrite + predictable landing
       | f.save('/srv/www/static/media/' + f.filename)
MEDIUM UPLOAD-WEBROOT-SAVE        hostile/filter.py:9  upload destination under a statically-served segment (public/static/www/assets/media)
       | f.save('/srv/www/static/media/' + f.filename)
MEDIUM UPLOAD-ORIGINAL-NAME-PATH  hostile/gallery.php:5  client filename inside the save/join path — traversal + overwrite + predictable landing
       | $t = '/var/www/htdocs/gallery/' . $_FILES['pic']['name'];
MEDIUM UPLOAD-NO-VALIDATION       hostile/quiet.py:5  upload endpoint writes/stores files and the file has no validation marker anywhere — no ext/mime check, size bound, rename or sniffing
       | out.write(f.read())
MEDIUM UPLOAD-SVG-HTML-ALLOWED    hostile/thumbs.py:2  allow list admits svg — stored XSS served from your origin
       | OK_EXT = {'png', 'svg', 'jpg'}
MEDIUM UPLOAD-ORIGINAL-NAME-PATH  hostile/thumbs.py:7  client filename inside the save/join path — traversal + overwrite + predictable landing
       | f.save('/srv/thumbs/' + f.filename)
MEDIUM UPLOAD-NO-SIZE-LIMIT       hostile/upload.js:7  upload middleware constructed with no size bound — unbounded multipart = memory/disk exhaustion
       | const upload = multer({ dest: 'public/uploads' });
MEDIUM UPLOAD-WEBROOT-SAVE        hostile/upload.js:7  upload destination under a statically-served segment (public/static/www/assets/media)
       | const upload = multer({ dest: 'public/uploads' });
MEDIUM UPLOAD-ORIGINAL-NAME-PATH  hostile/upload.js:12  client filename inside the save/join path — traversal + overwrite + predictable landing
       | const p = path.join('/srv/app/public/uploads', req.file.originalname);
MEDIUM UPLOAD-WEBROOT-SAVE        hostile/upload.js:12  upload destination under a statically-served segment (public/static/www/assets/media)
       | const p = path.join('/srv/app/public/uploads', req.file.originalname);
LOW    UPLOAD-EXT-ONLY            hostile/extonly.js:9  extension-only validation — no magic-byte/content check; a renamed script clears an ext list
       | const e = path.extname(req.file.originalname).toLowerCase();
LOW    UPLOAD-EXT-ONLY            hostile/filter.py:6  extension-only validation — no magic-byte/content check; a renamed script clears an ext list
       | ext = f.filename.split('.')[-1].lower()
LOW    UPLOAD-EXT-ONLY            hostile/thumbs.py:6  extension-only validation — no magic-byte/content check; a renamed script clears an ext list
       | if f.filename.rsplit('.', 1)[-1].lower() in OK_EXT:

scope: deterministic pre-scan — flags upload constructions that miss or fake validation; not an audit, clean != upload-safe
