Vulnerability: [thi.ng/egf] Potential arbitrary code execution of `#gpg`-tagged property values ### Impact

Potential for arbitrary code execution in `#gpg`-tagged property values (only if `decrypt: true` option is enabled)

### Patches

[A fix](https://github.com/thi-ng/umbrella/commit/3e14765d6bfd8006742c9e7860bc7d58ae94dfa5) has already been released as v0.4.0

### Workarounds

By default, EGF parse functions do NOT attempt to decrypt values (since GPG is only available in non-browser env).

However, if GPG encrypted values are used/required:

1. Perform a regex search for `#gpg`-tagged values in the EGF source file/string and check for backtick (\`) chars in the encrypted value string
2. Replace/remove them or skip parsing if present...

### References

https://github.com/thi-ng/umbrella/security/advisories/GHSA-rj44-gpjc-29r7#advisory-comment-65261

### For more information

If you have any questions or comments about this advisory, please open an issue in the [thi.ng/umbrella repo](https://github.com/thi-ng/umbrella/issues), of which this package is part of.
Vulnerability ID: GHSA-rj44-gpjc-29r7
CWE: CWE-78
Severity score: 6.4
Project: https://github.com/thi-ng/umbrella
Published: 2021-04-06
File: packages/egf/src/tags.ts
Commit message: Merge pull request from GHSA-rj44-gpjc-29r7

fix(egf): update GPG invocation to avoid arb code exec
Commit author: Karsten Schmidt

Vulnerable version (before fix):
 import type { IObjectOf } from "@thi.ng/api"; import { maybeParseFloat, maybeParseInt, unescape } from "@thi.ng/strings"; import { base64Decode } from "@thi.ng/transducers-binary"; import { execSync } from "child_process"; import { readFileSync } from "fs"; import { resolve as resolvePath } from "path"; import { IS_NODE, NODE_ONLY, TagParser } from "./api"; export const BUILTINS: IObjectOf<TagParser> = {     gpg: IS_NODE         ? (_, body, ctx) =>               (ctx.opts.decrypt                   ? execSync(`echo "${body}" | gpg --decrypt`).toString()                   : body               ).trim()         : NODE_ONLY,

Fixed version (after fix):
 import type { IObjectOf } from "@thi.ng/api"; import { maybeParseFloat, maybeParseInt, unescape } from "@thi.ng/strings"; import { base64Decode } from "@thi.ng/transducers-binary"; import { execFileSync } from "child_process"; import { readFileSync } from "fs"; import { resolve as resolvePath } from "path"; import { IS_NODE, NODE_ONLY, TagParser } from "./api"; export const BUILTINS: IObjectOf<TagParser> = {     gpg: IS_NODE         ? (_, body, ctx) =>               (ctx.opts.decrypt                   ? execFileSync("gpg", ["--decrypt"], {                         input: body,                     }).toString()                   : body               ).trim()         : NODE_ONLY,

Patch:
patch: @@ -1,7 +1,7 @@
 import type { IObjectOf } from "@thi.ng/api";
 import { maybeParseFloat, maybeParseInt, unescape } from "@thi.ng/strings";
 import { base64Decode } from "@thi.ng/transducers-binary";
-import { execSync } from "child_process";
+import { execFileSync } from "child_process";
 import { readFileSync } from "fs";
 import { resolve as resolvePath } from "path";
 import { IS_NODE, NODE_ONLY, TagParser } from "./api";
@@ -24,7 +24,9 @@ export const BUILTINS: IObjectOf<TagParser> = {
     gpg: IS_NODE
         ? (_, body, ctx) =>
               (ctx.opts.decrypt
-                  ? execSync(`echo "${body}" | gpg --decrypt`).toString()
+                  ? execFileSync("gpg", ["--decrypt"], {
+                        input: body,
+                    }).toString()
                   : body
               ).trim()
         : NODE_ONLY,
path: packages/egf/src/tags.ts