Vulnerability: Missing Release of Memory after Effective Lifetime in detect-character-encoding ### Impact

In detect-character-encoding v0.3.0 and earlier, allocated memory is not released.

### Patches

The problem has been patched in [detect-character-encoding v0.3.1](https://github.com/sonicdoe/detect-character-encoding/releases/tag/v0.3.1).

### CVSS score

[CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/RL:O/RC:C](https://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/RL:O/RC:C)

Base Score: 7.5 (High)
Temporal Score: 7.2 (High)

Since detect-character-encoding is a library, the scoring is based on the “[reasonable worst-case implementation scenario](https://www.first.org/cvss/v3.1/user-guide#3-7-Scoring-Vulnerabilities-in-Software-Libraries-and-Similar)”, namely, using detect-character-encoding in a program accessible over the internet which becomes unavailable when running out of memory. Depending on your specific implementation, the vulnerability’s severity in your program may be different.

### Proof of concept

```js
const express = require("express");
const detectCharacterEncoding = require("detect-character-encoding");

const app = express();

app.get("/", (req, res) => {
  detectCharacterEncoding(Buffer.from("foo"));

  res.end();
});

app.listen(3000);
```

`hey -n 1000000 http://localhost:3000` ([`hey`](https://github.com/rakyll/hey)) causes the Node.js process to consume more and more memory.

### References

- https://github.com/sonicdoe/detect-character-encoding/commit/d44356927b92e3b13e178071bf6d7c671766f588
- https://github.com/sonicdoe/detect-character-encoding/pull/6
Vulnerability ID: GHSA-5rwj-j5m3-3chj
CWE: CWE-401
Severity score: 7.5
Project: https://github.com/sonicdoe/detect-character-encoding
Published: 2021-09-01
File: icuWrapper.cpp
Commit message: Fix memory leak by properly closing `charsetDetector`
Commit author: Michael Hertsch

Vulnerable version (before fix):
 NAN_METHOD(DetectCharacterEncoding) {  	if(U_FAILURE(errorCode)) { 		Nan::ThrowError("Failed to set ICU charset detector’s text."); 		return; 	}  	charsetMatch = ucsdet_detect(charsetDetector, &errorCode);  	if(U_FAILURE(errorCode)) { 		Nan::ThrowError("Failed to detect charset."); 		return; 	}  	const char *charsetName = ucsdet_getName(charsetMatch, &errorCode);  	if(U_FAILURE(errorCode)) { 		Nan::ThrowError("Failed to get name from charset match."); 		return; 	}  	int32_t confidence = ucsdet_getConfidence(charsetMatch, &errorCode);  	if(U_FAILURE(errorCode)) { 		Nan::ThrowError("Failed to get confidence from charset match."); 		return; 	}  NAN_METHOD(DetectCharacterEncoding) { 	obj->Set(Nan::New<v8::String>("confidence").ToLocalChecked(), Nan::New<v8::Number>(confidence));  	info.GetReturnValue().Set(obj); }  void Init(v8::Local<v8::Object> exports) {

Fixed version (after fix):
 NAN_METHOD(DetectCharacterEncoding) {  	if(U_FAILURE(errorCode)) { 		Nan::ThrowError("Failed to set ICU charset detector’s text."); 		ucsdet_close(charsetDetector); 		return; 	}  	charsetMatch = ucsdet_detect(charsetDetector, &errorCode);  	if(U_FAILURE(errorCode)) { 		Nan::ThrowError("Failed to detect charset."); 		ucsdet_close(charsetDetector); 		return; 	}  	const char *charsetName = ucsdet_getName(charsetMatch, &errorCode);  	if(U_FAILURE(errorCode)) { 		Nan::ThrowError("Failed to get name from charset match."); 		ucsdet_close(charsetDetector); 		return; 	}  	int32_t confidence = ucsdet_getConfidence(charsetMatch, &errorCode);  	if(U_FAILURE(errorCode)) { 		Nan::ThrowError("Failed to get confidence from charset match."); 		ucsdet_close(charsetDetector); 		return; 	}  NAN_METHOD(DetectCharacterEncoding) { 	obj->Set(Nan::New<v8::String>("confidence").ToLocalChecked(), Nan::New<v8::Number>(confidence));  	info.GetReturnValue().Set(obj); 	ucsdet_close(charsetDetector); }  void Init(v8::Local<v8::Object> exports) {

Patch:
patch: @@ -28,27 +28,31 @@ NAN_METHOD(DetectCharacterEncoding) {
 
 	if(U_FAILURE(errorCode)) {
 		Nan::ThrowError("Failed to set ICU charset detector’s text.");
+		ucsdet_close(charsetDetector);
 		return;
 	}
 
 	charsetMatch = ucsdet_detect(charsetDetector, &errorCode);
 
 	if(U_FAILURE(errorCode)) {
 		Nan::ThrowError("Failed to detect charset.");
+		ucsdet_close(charsetDetector);
 		return;
 	}
 
 	const char *charsetName = ucsdet_getName(charsetMatch, &errorCode);
 
 	if(U_FAILURE(errorCode)) {
 		Nan::ThrowError("Failed to get name from charset match.");
+		ucsdet_close(charsetDetector);
 		return;
 	}
 
 	int32_t confidence = ucsdet_getConfidence(charsetMatch, &errorCode);
 
 	if(U_FAILURE(errorCode)) {
 		Nan::ThrowError("Failed to get confidence from charset match.");
+		ucsdet_close(charsetDetector);
 		return;
 	}
 
@@ -57,6 +61,7 @@ NAN_METHOD(DetectCharacterEncoding) {
 	obj->Set(Nan::New<v8::String>("confidence").ToLocalChecked(), Nan::New<v8::Number>(confidence));
 
 	info.GetReturnValue().Set(obj);
+	ucsdet_close(charsetDetector);
 }
 
 void Init(v8::Local<v8::Object> exports) {
path: icuWrapper.cpp