Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: sanitize user input (#128) #129

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

138 changes: 93 additions & 45 deletions src/components/form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,94 @@ import { useState, useEffect } from "react";

const Form = () => {
const [data, setData] = useState(null);
const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = useState(false);
const [error, setError] = useState(null);
const [userInput, setUserInput] = useState("");
const [isUserInputBlank, setIsUserInputBlank] = useState(false);

const fetchData = (e) => {
e.preventDefault();
const url = `/server/db/${userInput}.json`;

if (userInput.trim().length === 0) {
setIsUserInputBlank(true);
} else {
fetch(`${url}`)
.then((response) => {
if (response.status === 404) {
setIsUserInputBlank(false);
setErrorMessage(true);
} else if (!response.ok) {
throw Error("Resource not found");
const getDefinitionFromDb = async (query, encodeBeforeSearch = true) => {
// We need to encode twice to prevent the browser from decoding the query
// before actually fetching the file!
const dbFilename = encodeBeforeSearch
? encodeURIComponent(encodeURIComponent(query.toLowerCase()))
: encodeURIComponent(query);
const url = `/server/db/${dbFilename}.json`;

try {
const dbQuery = await fetch(url);

if (dbQuery.status === 404) {
return null;
}

return await dbQuery.json();
} catch (err) {
console.log(err.message);
return undefined;
}
};

const fetchData = async (query) => {
if (query.trim().length === 0) {
setError("emptyQuery");
return;
}

let data = await getDefinitionFromDb(query);

if (data) {
setError(null);
setData(data);
return;
}

try {
const mappingsFile = await fetch("/server/encodedAbbrMappings.json");
const mappings = await mappingsFile.json();

for (const encodedAbbr in mappings) {
const slang = mappings[encodedAbbr];

if (slang == query) {
data = await getDefinitionFromDb(encodedAbbr, false);

if (data) {
setError(null);
setData(data);
break; // Prevent O(n) as early as possible
}
return response.json();
})
.then((data) => {
setData(data);
setError(false);
setErrorMessage(false);
setIsUserInputBlank(false);
})
.catch((err) => {
console.log(err.message);
setErrorMessage(true);
setIsUserInputBlank(false);
});
}
}
} catch {
data = undefined;
} finally {
if (!data) {
setError(data === null ? "slangNotFound" : "unknown");
}
}
};

const handleSubmit = async (e) => {
e.preventDefault();

await fetchData(userInput);
};

const sanitizeInput = (value) => {
return value.trim().toLowerCase();
};

const handlePaste = (event) => {
return sanitizeInput(event.target.value);
};

const handleChange = (event) => {
const userInput = event.target.value;
const validInput = sanitizeInput(userInput);
setUserInput(validInput);
};

useEffect(() => {
setErrorMessage("");
setData(false);
setIsUserInputBlank(false);
setError(false);
setData(null);
setError(null);
}, [userInput]);

return (
Expand All @@ -58,9 +105,11 @@ const Form = () => {
translate to 'I don't know', and not the other way round.
</p>
</div>

<div className="mt-2 md:mt-0">
<form className="block md:flex items-center gap-3" id="form">
<div>
<form
onSubmit={handleSubmit}
className="block md:flex items-center gap-3"
id="form">
<div className="bg-ash h-11 rounded-full flex items-center p-3">
<svg
xmlns="http://www.w3.org/2000/svg"
Expand All @@ -81,15 +130,14 @@ const Form = () => {
placeholder="Search slang full meaning..."
className="flex-1 w-1/2 h-11 rounded-full ml-2 border-none outline-none text-gray text-lg bg-ash"
value={userInput}
onChange={(e) =>
setUserInput(e.target.value.toLocaleLowerCase())
}
onChange={handleChange}
onPaste={handlePaste}
/>
</div>

<button
onClick={fetchData}
className="bg-deeppurple text-ash font-bold rounded-xl hover:scale-110 p-2 mt-2 md:mt-0">
className="bg-deeppurple text-ash font-bold rounded-xl hover:scale-110 p-2 mt-2 md:mt-0"
type="submit">
Submit
</button>
</form>
Expand All @@ -108,21 +156,21 @@ const Form = () => {
</div>
)}

{error && (
{error === "unknown" && (
<div className="text-purple text-sm mt-2">
Oops. Some connection error occured.
</div>
)}

{isUserInputBlank && (
{error === "emptyQuery" && (
<div className="mt-4">
<p className="text-purple">
Search bar 🔍 is Empty! Please input a slang.
</p>
</div>
)}

{errorMessage && (
{error === "slangNotFound" && (
<div className="mt-4">
<p className="text-purple">
This entry does not exist in our records as of yet :(
Expand Down