From 67925c72e027a1c2cdad55bf10bfef28bc938ddb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:25:14 +0000 Subject: [PATCH 1/2] Initial plan From 14f1a7c1aea292d994ce096b021eda9e688638c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:28:11 +0000 Subject: [PATCH 2/2] Add sanitizeInput(), improve normalizeStreet(), update app.js and index.html Agent-Logs-Url: https://github.com/NickHamby/PotholeDodgerV2/sessions/be13c8ab-ca4e-4894-a848-cc4155488651 Co-authored-by: NickHamby <271342652+NickHamby@users.noreply.github.com> --- web/index.html | 1 + web/js/app.js | 6 +++--- web/js/hazards.js | 35 +++++++++++++++++++++++++++-------- web/js/sanitize.js | 28 ++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 web/js/sanitize.js diff --git a/web/index.html b/web/index.html index 8f6bd86..024aade 100644 --- a/web/index.html +++ b/web/index.html @@ -31,6 +31,7 @@

Pothole Dodger

+ diff --git a/web/js/app.js b/web/js/app.js index 47b336b..6f9af54 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -13,7 +13,7 @@ function attachAutocomplete(inputEl) { inputEl.addEventListener('input', function () { clearTimeout(debounceTimer); - const query = inputEl.value.trim(); + const query = sanitizeInput(inputEl.value); if (query.length < 3) { hideList(); @@ -72,8 +72,8 @@ document.addEventListener('DOMContentLoaded', function () { } async function run() { - const origin = document.getElementById('origin').value.trim(); - const destination = document.getElementById('destination').value.trim(); + const origin = sanitizeInput(document.getElementById('origin').value); + const destination = sanitizeInput(document.getElementById('destination').value); if (!origin || !destination) { setStatus('Please enter both an origin and destination.'); diff --git a/web/js/hazards.js b/web/js/hazards.js index 48cbe51..d499544 100644 --- a/web/js/hazards.js +++ b/web/js/hazards.js @@ -1,6 +1,6 @@ // hazards.js — loads hazard data and filters it to streets matching the route -const ABBR_MAP = [ +const STREET_TYPE_ABBRS = [ [/\bSt\b/g, 'Street'], [/\bAve\b/g, 'Avenue'], [/\bBlvd\b/g, 'Boulevard'], @@ -9,21 +9,40 @@ const ABBR_MAP = [ [/\bPkwy\b/g, 'Parkway'], [/\bLn\b/g, 'Lane'], [/\bCt\b/g, 'Court'], - [/\bW\b/g, 'West'], - [/\bE\b/g, 'East'], - [/\bN\b/g, 'North'], - [/\bS\b/g, 'South'], ]; +const DIRECTIONAL_EXPAND = { + W: 'West', + E: 'East', + N: 'North', + S: 'South', +}; + function normalizeStreet(str) { let s = str; - for (const [pattern, replacement] of ABBR_MAP) { + + // 1. Strip trailing ZIP code (5 digits, optionally preceded by comma and/or space) + s = s.replace(/[,\s]+\d{5}\s*$/, ''); + + // 2. Expand street type abbreviations + for (const [pattern, replacement] of STREET_TYPE_ABBRS) { s = s.replace(pattern, replacement); } - // Strip leading house numbers (including fractional like "8 1/2") but preserve - // street numbers that are part of the name (e.g. "1st Avenue") + + // 3. Expand directionals at start of string (prefix directional) + s = s.replace(/^(W|E|N|S)\b\s*/, (_, d) => DIRECTIONAL_EXPAND[d] + ' '); + + // 4. Expand directionals at end of string (suffix directional) + s = s.replace(/\s+(W|E|N|S)$/, (_, d) => ' ' + DIRECTIONAL_EXPAND[d]); + + // 5. Strip leading house numbers (including fractional like "8 1/2") s = s.replace(/^\d+(\s+\d+\/\d+)?\s+/, ''); + + // 6. Strip remaining punctuation s = s.replace(/[^a-zA-Z0-9\s]/g, ''); + + // 7. Collapse whitespace and trim, lowercase + s = s.replace(/\s{2,}/g, ' '); return s.trim().toLowerCase(); } diff --git a/web/js/sanitize.js b/web/js/sanitize.js new file mode 100644 index 0000000..7ab3395 --- /dev/null +++ b/web/js/sanitize.js @@ -0,0 +1,28 @@ +// sanitize.js — sanitizes raw user input before geocoding or internal matching + +/** + * Sanitizes a raw user-supplied address string for use as a Nominatim query. + * + * - Trims leading/trailing whitespace + * - Collapses multiple spaces into one + * - Strips parenthetical context (e.g. "(near the park)") + * - Removes characters that break queries: # " ' . ; : ! ? @ ^ * [ ] { } | \ ~ ` = + < > % & _ + * - Preserves commas (Nominatim field separators), hyphens (address ranges), slashes (fractions) + * - Does NOT expand abbreviations (leave that to normalizeStreet in hazards.js) + * - Does NOT strip ZIP codes (Nominatim handles them; strip only inside normalizeStreet) + * - Must never be called on coordinate (lat/lng) values + * + * @param {string} str Raw input string + * @returns {string} Sanitized string safe for Nominatim queries + */ +function sanitizeInput(str) { + if (typeof str !== 'string') return ''; + let s = str; + // Remove parenthetical content + s = s.replace(/\([^)]*\)/g, ''); + // Keep: letters, digits, spaces, commas, hyphens, forward slashes + s = s.replace(/[^a-zA-Z0-9\s,\-\/]/g, ''); + // Collapse multiple spaces + s = s.replace(/\s{2,}/g, ' '); + return s.trim(); +}