Repository: Automattic/node-canvas

Issue:
loadImage causes the process to terminate
## Issue
A problem appeared in one of the projects, the process was silently terminating. In the end, the reason was determined - `loadImage`. A URL object was passed to the function as src instead of a string or buffer. But, what is strange is the process termination with no way to understand what is happening. Even the debugger (Node.js) could not help.

I think it would be more informative to throw an exception in `canvas/lib/image.js` when an invalid parameter is passed to setter. At the moment it is checking for a string or buffer and nothing else.

I was expecting an exception to be thrown that I could catch or at least a process crash with some information about the problem, but the process silently terminates. Is this a problem in bindings? Can someone explain the mechanism of this error? (I don't know C++ and bindings.)

## Steps to Reproduce
```js
const { loadImage } = require("canvas");
const { URL } = require("node:url");

async function main() {

  console.log("Start...");

  try {

    const userPhotoUrl = new URL("https://placehold.co/600x400");

    await loadImage(userPhotoUrl);

    console.log("Image loaded!");

  } catch (err) {

    console.error(err);

  }

  console.log("Done.");

}

main();

/*

Expected:
Start...
Image loaded!
Done.

Or:
Start...
<ERROR>
Done.

Result:
Start...

*/
```

## Your Environment
OS: Ubuntu 22.04.3 LTS
Node.js: 16.16.0, 18.12.1, 18.16.1
node-canvas: 2.11.2, 2.10.0, 2.8.0, 2.6.0



Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b16096f69..aafa96fe8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ project adheres to [Semantic Versioning](http://semver.org/).
 ### Added
 ### Fixed
 * `roundRect()` shape incorrect when radii were large relative to rectangle size (#2400)
+* Reject loadImage when src is null or invalid (#2304)
 
 3.2.0
 ==================
diff --git a/lib/image.js b/lib/image.js
index 9ffa3c794..72243439c 100644
--- a/lib/image.js
+++ b/lib/image.js
@@ -63,6 +63,10 @@ Object.defineProperty(Image.prototype, 'src', {
       }
     } else if (Buffer.isBuffer(val)) {
       setSource(this, val)
+    } else {
+      const err = new Error("Invalid image source")
+      if (typeof this.onerror === 'function') this.onerror(err)
+      else throw err
     }
   },
 


Test patch:
diff --git a/test/image.test.js b/test/image.test.js
index a5d6f415c..edae78beb 100644
--- a/test/image.test.js
+++ b/test/image.test.js
@@ -516,6 +516,24 @@ describe('Image', function () {
       img.src = path.join(bmpDir, 'bomb.bmp')
     })
 
+    it('rejects when loadImage is called with null', async function () {
+        await assert.rejects(
+            loadImage(null),
+        )
+    })
+
+    it('rejects when loadImage is called with undefined', async function () {
+        await assert.rejects(
+            loadImage(undefined),
+        )
+    })
+
+    it('rejects when loadImage is called with empty string', async function () {
+        await assert.rejects(
+            loadImage(''),
+        )
+    })
+
     function testImgd (img, data) {
       const ctx = createCanvas(img.width, img.height).getContext('2d')
       ctx.drawImage(img, 0, 0)