CVE-2022-4399: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: A vulnerability was found in TicklishHoneyBee nodau. It has been rated as critical. Affected by this issue is some unknown functionality of the file src/db.c. The manipulation of the argument value/name leads to sql injection. The name of the patch is 7a7d737a3929f335b9717ddbd31db91151b69ad2. It is recommended to apply a patch to fix this issue. The identifier of this vulnerability is VDB-215252.
CWE description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
CVSS v3 base score: 9.8
Published: 2022-12-10T22:15Z
Repository: https://github.com/TicklishHoneyBee/nodau
Language: C
Files changed: CHANGELOG, src/db.c, src/edit.c
Commit date: 2022-03-03 14:02:37 +0200
Commit message: Use parametetrized SQL statement

Move to parametetrized SQL statement allows using chars like "'" in
notes and also avoid sql injection

Diff: diff --git a/CHANGELOG b/CHANGELOG
index ae4429b..76582c0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -83,5 +83,8 @@ CHANGES:
 	- fix segfault when running 'nodau list' with search keyword,
 	 and there are no matches (by Omer Dagan
 	 <mr.omer.dagan <at> gmail.com>)
 	- remove variable definitions in header file that caused issues
 	 with gcc 10
+0.3.10
+    - Move to parametetrized SQL statement allows using chars like "'"
+     in notes, and also avoid sql injection
diff --git a/src/db.c b/src/db.c
index a969596..364046b 100644
--- a/src/db.c
+++ b/src/db.c
@@ -283,22 +283,40 @@ int db_update(char* name, char* value)
 	int r = 0;
 	/* create the sql statement using the name/text for this note
 	 * if it's meant to be encrypted, then crypt_key will be set */
 	if (crypt_key) {
 		value = note_encrypt(value,crypt_key);
-		r = asprintf(&sql, "UPDATE nodau set text='%s' , encrypted='true' WHERE name='%s'", value, name);
-		free(value);
-		if (r < 0)
+		if (asprintf(&sql, "UPDATE nodau set text=?, encrypted='true' WHERE name=?") < 0)
 			return 1;
 	}else{
-		if (asprintf(&sql, "UPDATE nodau set text='%s' , encrypted='false' WHERE name='%s'", value, name) < 0)
+		if (asprintf(&sql, "UPDATE nodau set text=?, encrypted='false' WHERE name=?") < 0)
 			return 1;
 	}
 
+	sqlite3_stmt *compiled_statement;
+	r = sqlite3_prepare_v2(db_data.db, sql, -1, &compiled_statement, NULL);
+	if (r != SQLITE_OK)
+		return 1;
+
+	r= sqlite3_bind_text(compiled_statement, 1, value, -1, NULL);
+	r= sqlite3_bind_text(compiled_statement, 2, name, -1, NULL);
+	if (r != SQLITE_OK)
+		return 1;
+
 	/* do it */
-	r = sqlite3_exec(db_data.db, sql, NULL, 0, &db_data.error_msg);
+	r = sqlite3_step(compiled_statement);
+	if (r != SQLITE_DONE) {
+		fprintf(stderr, "Error #%d: %s\n", r, db_err());
+		return 1;
+	}
+	r = sqlite3_finalize(compiled_statement);
+	if (r != SQLITE_OK)
+		fprintf(stderr, "Error #%d: %s\n", r, db_err());
+
 	free(sql);
+	if (crypt_key)
+		free(value);
 	return r;
 }
 
 /* list notes according to search criteria */
 int db_list(char* search)
diff --git a/src/edit.c b/src/edit.c
index 2ef0334..672c2dc 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -191,11 +191,12 @@ static int edit_ext(char* editor, char* name, char* date, char* data)
 				/* find the note data */
 				l = strstr(b,"-----");
 				if (l) {
 					/* save the note */
 					l += 6;
-					if (db_update(name,l))
+					int r = db_update(name,l);
+					if (r != SQLITE_DONE && r != SQLITE_OK)
 						return 1;
 
 					/* let the user know */
 					printf("%s saved\n",name);
 				}

Vulnerable code: 		r = asprintf(&sql, "UPDATE nodau set text='%s' , encrypted='true' WHERE name='%s'", value, name);
		free(value);
		if (r < 0)
		if (asprintf(&sql, "UPDATE nodau set text='%s' , encrypted='false' WHERE name='%s'", value, name) < 0)
	r = sqlite3_exec(db_data.db, sql, NULL, 0, &db_data.error_msg);
					if (db_update(name,l))
Fixed code: 0.3.10
    - Move to parametetrized SQL statement allows using chars like "'"
     in notes, and also avoid sql injection
		if (asprintf(&sql, "UPDATE nodau set text=?, encrypted='true' WHERE name=?") < 0)
		if (asprintf(&sql, "UPDATE nodau set text=?, encrypted='false' WHERE name=?") < 0)
	sqlite3_stmt *compiled_statement;
	r = sqlite3_prepare_v2(db_data.db, sql, -1, &compiled_statement, NULL);
	if (r != SQLITE_OK)
		return 1;

	r= sqlite3_bind_text(compiled_statement, 1, value, -1, NULL);
	r= sqlite3_bind_text(compiled_statement, 2, name, -1, NULL);
	if (r != SQLITE_OK)
		return 1;

	r = sqlite3_step(compiled_statement);
	if (r != SQLITE_DONE) {
		fprintf(stderr, "Error #%d: %s\n", r, db_err());
		return 1;
	}
	r = sqlite3_finalize(compiled_statement);
	if (r != SQLITE_OK)
		fprintf(stderr, "Error #%d: %s\n", r, db_err());

	if (crypt_key)
		free(value);
					int r = db_update(name,l);
					if (r != SQLITE_DONE && r != SQLITE_OK)