CVE-2022-29256: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
value: sharp is an application for Node.js image processing. Prior to version 0.30.5, there is a possible vulnerability in logic that is run only at `npm install` time when installing versions of `sharp` prior to the latest v0.30.5. If an attacker has the ability to set the value of the `PKG_CONFIG_PATH` environment variable in a build environment then they might be able to use this to inject an arbitrary command at `npm install` time. This is not part of any runtime code, does not affect Windows users at all, and is unlikely to affect anyone that already cares about the security of their build environment. This problem is fixed in version 0.30.5.
CWE description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
CVSS v3 base score: 6.7
Published: 2022-05-25T22:15Z
Repository: https://github.com/lovell/sharp
Language: JavaScript
Files changed: docs/changelog.md, lib/libvips.js
Commit date: 2022-05-23 12:12:19 +0100
Commit message: Install: pass `PKG_CONFIG_PATH` via env rather than substitution

Diff: diff --git a/docs/changelog.md b/docs/changelog.md
index fb275dd..682221f 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,10 +4,13 @@
 
 Requires libvips v8.12.2
 
 ### v0.30.5 - TBD
 
+* Install: pass `PKG_CONFIG_PATH` via env rather than substitution.
+  [@dwisiswant0](https://github.com/dwisiswant0)
+
 * Allow installation of prebuilt libvips binaries from filesystem.
   [#3196](https://github.com/lovell/sharp/pull/3196)
   [@ankurparihar](https://github.com/ankurparihar)
 
 * Fix rotate-then-extract for EXIF orientation 2.
diff --git a/lib/libvips.js b/lib/libvips.js
index e13db20..3159a9b 100644
--- a/lib/libvips.js
+++ b/lib/libvips.js
@@ -63,11 +63,16 @@ const isRosetta = function () {
   return false;
 };
 
 const globalLibvipsVersion = function () {
   if (process.platform !== 'win32') {
-    const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
+    const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
+      ...spawnSyncOptions,
+      env: {
+        PKG_CONFIG_PATH: pkgConfigPath()
+      }
+    }).stdout;
     /* istanbul ignore next */
     return (globalLibvipsVersion || '').trim();
   } else {
     return '';
   }
@@ -83,11 +88,14 @@ const removeVendoredLibvips = function () {
   rm(vendorPath, { recursive: true, maxRetries: 3, force: true });
 };
 
 const pkgConfigPath = function () {
   if (process.platform !== 'win32') {
-    const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
+    const brewPkgConfigPath = spawnSync(
+      'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
+      spawnSyncOptions
+    ).stdout || '';
     return [
       brewPkgConfigPath.trim(),
       env.PKG_CONFIG_PATH,
       '/usr/local/lib/pkgconfig',
       '/usr/lib/pkgconfig',

Vulnerable code:     const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
    const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
Fixed code: * Install: pass `PKG_CONFIG_PATH` via env rather than substitution.
  [@dwisiswant0](https://github.com/dwisiswant0)

    const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
      ...spawnSyncOptions,
      env: {
        PKG_CONFIG_PATH: pkgConfigPath()
      }
    }).stdout;
    const brewPkgConfigPath = spawnSync(
      'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
      spawnSyncOptions
    ).stdout || '';