From e4c6afc774ef140a7940c31b6793b79b4139c0a0 Mon Sep 17 00:00:00 2001 From: Ankit Kumar Singh <160294709+AskitEndo@users.noreply.github.com> Date: Tue, 8 Oct 2024 22:16:57 +0530 Subject: [PATCH 1/3] Fixes : #49 Better Url Implementation --- src/components/Manager.jsx | 136 ++++++++++++++++++++++++++----------- 1 file changed, 98 insertions(+), 38 deletions(-) diff --git a/src/components/Manager.jsx b/src/components/Manager.jsx index eb25883..3205db0 100755 --- a/src/components/Manager.jsx +++ b/src/components/Manager.jsx @@ -16,6 +16,8 @@ const Manager = () => { const [passwordArray, setPasswordArray] = useState([]); const [passwordErrors, setPasswordErrors] = useState([]); const [isTyping, setIsTyping] = useState(false); + const [urlErrors, setUrlErrors] = useState([]); + const [formErrors, setFormErrors] = useState([]); const getPasswords = async () => { let req = await fetch("http://localhost:3000/"); @@ -84,49 +86,77 @@ const Manager = () => { return errors.length === 0; }; - const savePassword = async () => { - if ( - form.site.length > 3 && - form.username.length >= 3 && - validatePassword(form.password) - ) { - if (form.id) { - const updatedPasswords = passwordArray.map((item) => - item._id === form.id ? { ...form } : item - ); - setPasswordArray(updatedPasswords); - await fetch(`http://localhost:3000/${form.id}`, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(form), - }); - } else { - const newPassword = { ...form, id: uuidv4() }; - setPasswordArray([...passwordArray, newPassword]); - await fetch("http://localhost:3000/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(newPassword), - }); - } + const errors = []; - setForm({ id: "", site: "", username: "", password: "" }); - toast("Password saved!", { - position: "top-right", - autoClose: 5000, - hideProgressBar: false, - closeOnClick: true, - pauseOnHover: true, - draggable: true, - progress: undefined, - theme: "dark", + // Validate Site URL + if (form.site.length <= 3 || !validateURL(form.site)) { + errors.push( + "Error: Invalid site name. Ensure it meets the required format." + ); + } + + // Validate Username + if (form.username.length < 3) { + errors.push("Error: Username must be at least 3 characters long."); + } + + // Validate Password + const passwordValidationResult = validatePassword(form.password); + if (!passwordValidationResult) { + errors.push("Error: Password does not meet the required criteria."); + } + + // If there are errors, show them in toast and return + if (errors.length > 0) { + errors.forEach((error) => + toast(error, { + position: "top-right", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }) + ); + return; // Exit if there are validation errors + } + + // Proceed to save the password if all validations pass + if (form.id) { + const updatedPasswords = passwordArray.map((item) => + item._id === form.id ? { ...form } : item + ); + setPasswordArray(updatedPasswords); + await fetch(`http://localhost:3000/${form.id}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(form), }); } else { - toast( - "Error: Password not saved! Ensure the password meets all criteria." - ); + const newPassword = { ...form, id: uuidv4() }; + setPasswordArray([...passwordArray, newPassword]); + await fetch("http://localhost:3000/", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(newPassword), + }); } + + // Clear form and show success toast + setForm({ id: "", site: "", username: "", password: "" }); + toast("Password saved!", { + position: "top-right", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); }; const deletePassword = async (id) => { @@ -160,10 +190,31 @@ const Manager = () => { const handleChange = (e) => { setForm({ ...form, [e.target.name]: e.target.value }); + if (e.target.name === "password") { validatePassword(e.target.value); setIsTyping(e.target.value.length > 0); } + + if (e.target.name === "site") { + validateURL(e.target.value); + } + }; + + const validateURL = (url) => { + const urlPattern = + /^(https?:\/\/)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}(\/\S*)?$/; + let errors = []; + + if (!urlPattern.test(url)) { + errors.push("Invalid URL format. Must be in format: example.com"); + } + if (url.length === 0) { + errors.push("URL field cannot be empty."); + } + setUrlErrors(errors); + + return errors.length === 0; }; return ( @@ -192,6 +243,15 @@ const Manager = () => { name="site" id="site" /> + {urlErrors.length > 0 && ( +
+ +
+ )}
Date: Wed, 9 Oct 2024 15:25:41 +0530 Subject: [PATCH 2/3] Fixes : #49 Better URL v2 --- src/components/Manager.jsx | 46 ++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/components/Manager.jsx b/src/components/Manager.jsx index 3205db0..a7410fe 100755 --- a/src/components/Manager.jsx +++ b/src/components/Manager.jsx @@ -17,7 +17,10 @@ const Manager = () => { const [passwordErrors, setPasswordErrors] = useState([]); const [isTyping, setIsTyping] = useState(false); const [urlErrors, setUrlErrors] = useState([]); - const [formErrors, setFormErrors] = useState([]); + const [isSiteFocused, setIsSiteFocused] = useState(false); // To track input focus + + const handleSiteFocus = () => setIsSiteFocused(true); + const handleSiteBlur = () => setIsSiteFocused(false); const getPasswords = async () => { let req = await fetch("http://localhost:3000/"); @@ -234,24 +237,29 @@ const Manager = () => {

- - {urlErrors.length > 0 && ( -
-
    - {urlErrors.map((error, index) => ( -
  • {error}
  • - ))} -
-
- )} +
+ + {isSiteFocused && urlErrors.length > 0 && ( +
+
    + {urlErrors.map((error, index) => ( +
  • {error}
  • + ))} +
+
+ )} +
+
Date: Wed, 9 Oct 2024 19:45:27 +0530 Subject: [PATCH 3/3] Fixes : #62 Fixex the Hash being imported as passwrds --- backend/server.js | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/backend/server.js b/backend/server.js index 06bb253..296cabb 100755 --- a/backend/server.js +++ b/backend/server.js @@ -9,27 +9,34 @@ const crypto = require("crypto"); const ENCRYPTION_KEY = crypto.randomBytes(32); // Must be 256 bits (32 bytes) const IV_LENGTH = 16; // For AES, this is always 16 - // Encrypt a password const encrypt = (text) => { const iv = crypto.randomBytes(IV_LENGTH); - const cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(ENCRYPTION_KEY), iv); + const cipher = crypto.createCipheriv( + "aes-256-cbc", + Buffer.from(ENCRYPTION_KEY), + iv + ); let encrypted = cipher.update(text, "utf8", "hex"); encrypted += cipher.final("hex"); return iv.toString("hex") + ":" + encrypted; // Store IV with the encrypted password }; // Decrypt function -function decrypt(text) { - let ivBuffer = Buffer.from(text.iv, "hex"); - let encryptedText = text.encryptedData; - - let decipher = crypto.createDecipheriv("aes-256-cdc", Buffer.from(ENCRYPTION_KEY), ivBuffer); - let decrypted = decipher.update(encryptedText, "hex", "utf-8"); +const decrypt = (text) => { + const [iv, encryptedData] = text.split(":"); + const ivBuffer = Buffer.from(iv, "hex"); + + const decipher = crypto.createDecipheriv( + "aes-256-cbc", + Buffer.from(ENCRYPTION_KEY), + ivBuffer + ); + let decrypted = decipher.update(encryptedData, "hex", "utf-8"); decrypted += decipher.final("utf-8"); return decrypted; -} +}; dotenv.config(); @@ -84,7 +91,11 @@ app.post("/", async (req, res) => { const collection = db.collection("passwords"); // Encrypt the password before saving const encryptedPassword = encrypt(password); - const result = await collection.insertOne({ site, username, password: encryptedPassword }); + const result = await collection.insertOne({ + site, + username, + password: encryptedPassword, + }); res.status(201).json({ success: true, result }); } catch (error) { console.error("Error saving password:", error); @@ -133,7 +144,6 @@ app.put("/:id", async (req, res) => { } }); - // Delete a password by id app.delete("/:id", async (req, res) => { try { @@ -174,9 +184,16 @@ app.get("/export", async (req, res) => { const db = client.db(dbName); const passwords = await db.collection("passwords").find({}).toArray(); + // Decrypt each password before exporting + const decryptedPasswords = passwords.map((password) => ({ + site: password.site, + username: password.username, + password: decrypt(password.password), // Directly decrypt the stored password + })); + res.setHeader("content-Type", "application/json"); res.setHeader("content-disposition", "attachment; filename=passwords.json"); - res.status(200).json(passwords); + res.status(200).json(decryptedPasswords); } catch (error) { console.error("Error exporting passwords:", error); res