feat: Add Spotify-style backdrop, Cascade OCR, Smart Scan Flow & OCR Dashboard

- BottleGrid: Implement blurred backdrop effect for bottle cards
- Cascade OCR: TextDetector → RegEx → Fuzzy Match → window.ai pipeline
- Smart Scan: Native OCR for Android, Live Text fallback for iOS
- OCR Dashboard: Admin page at /admin/ocr-logs with stats and scan history
- Features: Add feature flags in src/config/features.ts
- SQL: Add ocr_logs table migration
- Services: Update analyze-bottle to use OpenRouter, add save-ocr-log
This commit is contained in:
2026-01-18 20:38:48 +01:00
parent 83e852e5fb
commit 9ba0825bcd
46 changed files with 3874 additions and 741 deletions

99
security-report.txt Normal file
View File

@@ -0,0 +1,99 @@
┌──────────────────┐
│ 15 Code Findings │
└──────────────────┘
public/sw.js
❱ javascript.lang.security.audit.unsafe-formatstring.unsafe-formatstring
Detected string concatenation with a non-literal variable in a util.format / console.log function.
If an attacker injects a format specifier in the string, it will forge the log message. Try to use
constant values for the format string.
Details: https://sg.run/7Y5R
75┆ console.error(`⚠️ PWA: Pre-cache failed for ${url}:`, error);
⋮┆----------------------------------------
174┆ console.error(`[SW] Failed to fetch ${url.pathname}:`, error);
scripts/scrape-distillery-tags.ts
❱ javascript.lang.security.audit.unsafe-formatstring.unsafe-formatstring
Detected string concatenation with a non-literal variable in a util.format / console.log function.
If an attacker injects a format specifier in the string, it will forge the log message. Try to use
constant values for the format string.
Details: https://sg.run/7Y5R
107┆ console.error(`❌ API Error for ${name}: ${response.status}`, data.error || data);
⋮┆----------------------------------------
116┆ console.error(`⚠️ OpenRouter Error for ${name}:`, data.error.message);
⋮┆----------------------------------------
119┆ console.error(`⚠️ No content returned for ${name}. Full response:`, JSON.stringify(data,
null, 2));
⋮┆----------------------------------------
125┆ console.error(`❌ Fetch Exception for ${name}:`, error);
src/context/AuthContext.tsx
❱ javascript.lang.security.audit.unsafe-formatstring.unsafe-formatstring
Detected string concatenation with a non-literal variable in a util.format / console.log function.
If an attacker injects a format specifier in the string, it will forge the log message. Try to use
constant values for the format string.
Details: https://sg.run/7Y5R
40┆ console.log(`[AuthContext] event: ${event}`, {
src/hooks/useScanner.ts
❱ javascript.lang.security.audit.unsafe-formatstring.unsafe-formatstring
Detected string concatenation with a non-literal variable in a util.format / console.log function.
If an attacker injects a format specifier in the string, it will forge the log message. Try to use
constant values for the format string.
Details: https://sg.run/7Y5R
157┆ console.log(`[useScanner] ${providerUsed} complete:`, cloudResult);
⋮┆----------------------------------------
186┆ console.warn(`[useScanner] ${providerUsed} failed:`, cloudResponse.error);
src/i18n/I18nContext.tsx
❯❱ javascript.lang.security.audit.prototype-pollution.prototype-pollution-loop.prototype-pollution-loop
Possibility of prototype polluting function detected. By adding or modifying attributes of an object
prototype, it is possible to create attributes that exist on every object, or replace critical
attributes with malicious ones. This can be problematic if the software depends on existence or non-
existence of certain attributes, or uses pre-defined attributes of object prototype (such as
hasOwnProperty, toString or valueOf). Possible mitigations might be: freezing the object prototype,
using an object without prototypes (via Object.create(null) ), blocking modifications of attributes
that resolve to object prototype, using Map instead of object.
Details: https://sg.run/w1DB
54┆ current = current[key];
src/lib/distillery-matcher.ts
❯❱ javascript.lang.security.audit.detect-non-literal-regexp.detect-non-literal-regexp
RegExp() called with a `distillery` function argument, this might allow an attacker to cause a
Regular Expression Denial-of-Service (ReDoS) within your application as RegExP blocks the main
thread. For this reason, it is recommended to use hardcoded regexes instead. If your regex is run on
user-controlled input, consider performing input validation or use a regex checking/sanitization
library such as https://www.npmjs.com/package/recheck to verify that the regex does not appear
vulnerable to ReDoS.
Details: https://sg.run/gr65
154┆ const regex = new RegExp(`^${escaped}\\s*[-–—:]?\\s*`, 'i');
⋮┆----------------------------------------
161┆ const anywhereRegex = new RegExp(`\\b${escaped}\\b\\s*[-–—:]?\\s*`, 'i');
src/services/bulk-scan.ts
❱ javascript.lang.security.audit.unsafe-formatstring.unsafe-formatstring
Detected string concatenation with a non-literal variable in a util.format / console.log function.
If an attacker injects a format specifier in the string, it will forge the log message. Try to use
constant values for the format string.
Details: https://sg.run/7Y5R
211┆ console.error(`Analysis failed for bottle ${bottleId}:`, error);
src/services/tags.ts
❱ javascript.lang.security.audit.unsafe-formatstring.unsafe-formatstring
Detected string concatenation with a non-literal variable in a util.format / console.log function.
If an attacker injects a format specifier in the string, it will forge the log message. Try to use
constant values for the format string.
Details: https://sg.run/7Y5R
33┆ console.error(`Error fetching tags for ${category}:`, error);
⋮┆----------------------------------------
39┆ console.error(`Exception in getTagsByCategory for ${category}:`, err);