VYPR
High severity8.8NVD Advisory· Published Apr 18, 2026· Updated Apr 24, 2026

CVE-2026-35582

CVE-2026-35582

Description

Emissary is a P2P based data-driven workflow engine. In versions 8.42.0 and below, Executrix.getCommand() is vulnerable to OS command injection because it interpolates temporary file paths into a /bin/sh -c shell command string without any escaping or input validation. The IN_FILE_ENDING and OUT_FILE_ENDING configuration keys flow directly into these paths, allowing a place author who can write or modify a .cfg file to inject arbitrary shell metacharacters that execute OS commands in the JVM process's security context. The framework already sanitizes placeName via an allowlist before embedding it in the same shell string, but applies no equivalent sanitization to file ending values. No runtime privileges beyond place configuration authorship, and no API or network access, are required to exploit this vulnerability. This is a framework-level defect with no safe mitigation available to downstream implementors, as Executrix provides neither escaping nor documented preconditions against metacharacters in file ending inputs. This issue has been fixed in version 8.43.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
gov.nsa.emissary:emissaryMaven
< 8.43.08.43.0

Affected products

1
  • cpe:2.3:a:nsa:emissary:*:*:*:*:*:*:*:*
    Range: <8.43.0

Patches

1
1faf33f2494c

Merge commit from fork

2 files changed · +117 4
  • src/main/java/emissary/util/shell/Executrix.java+20 4 modified
    @@ -77,6 +77,12 @@ public enum OUTPUT_TYPE {
     
         protected static final Pattern INVALID_PLACE_NAME_CHARS = Pattern.compile("[^a-zA-Z0-9_-]");
     
    +    /**
    +     * Allowlist for file ending values. A leading dot is permitted so that normal extensions like {@code .out} are accepted
    +     * unchanged; every other character must be alphanumeric, underscore, or hyphen.
    +     */
    +    protected static final Pattern INVALID_FILE_ENDING_CHARS = Pattern.compile("[^a-zA-Z0-9_.\\-]");
    +
         /**
          * Create using all defaults
          */
    @@ -118,8 +124,8 @@ protected void configure(@Nullable final Configurator configGArg) {
             final Configurator configG = (configGArg != null) ? configGArg : new ServiceConfigGuide();
     
             this.command = configG.findStringEntry("EXEC_COMMAND", "echo 'YouForGotToSetEXEC_COMMAND' | tee bla.txt");
    -        this.inFileEnding = configG.findStringEntry("IN_FILE_ENDING", "");
    -        this.outFileEnding = configG.findStringEntry("OUT_FILE_ENDING", this.inFileEnding.isEmpty() ? ".out" : "");
    +        this.inFileEnding = cleanFileEnding(configG.findStringEntry("IN_FILE_ENDING", ""));
    +        this.outFileEnding = cleanFileEnding(configG.findStringEntry("OUT_FILE_ENDING", this.inFileEnding.isEmpty() ? ".out" : ""));
             this.output = configG.findStringEntry("OUTPUT_TYPE", "STD");
             this.order = configG.findStringEntry("ORDER", "NORMAL");
             this.numArgs = configG.findStringEntry("NUM_ARGS", "");
    @@ -149,6 +155,16 @@ protected static String cleanPlaceName(final String placeName) {
             return INVALID_PLACE_NAME_CHARS.matcher(placeName).replaceAll("_");
         }
     
    +    /**
    +     * Remove invalid characters from a file ending value, see {@link Executrix#INVALID_FILE_ENDING_CHARS}
    +     *
    +     * @param fileEnding the raw file ending (e.g. {@code .out})
    +     * @return a cleaned string with shell-unsafe characters replaced by {@code _}
    +     */
    +    protected static String cleanFileEnding(final String fileEnding) {
    +        return INVALID_FILE_ENDING_CHARS.matcher(fileEnding).replaceAll("_");
    +    }
    +
         /**
          * Creates a set of temp file names (does not do any disk activity)
          *
    @@ -1174,7 +1190,7 @@ public String getInFileEnding() {
          * @param argInFileEnding Value to assign to this.inFileEnding
          */
         public void setInFileEnding(final String argInFileEnding) {
    -        this.inFileEnding = argInFileEnding;
    +        this.inFileEnding = cleanFileEnding(argInFileEnding);
         }
     
         /**
    @@ -1192,7 +1208,7 @@ public String getOutFileEnding() {
          * @param argOutFileEnding Value to assign to this.outFileEnding
          */
         public void setOutFileEnding(final String argOutFileEnding) {
    -        this.outFileEnding = argOutFileEnding;
    +        this.outFileEnding = cleanFileEnding(argOutFileEnding);
         }
     
         /**
    
  • src/test/java/emissary/util/shell/ExecutrixTest.java+97 0 modified
    @@ -1,5 +1,7 @@
     package emissary.util.shell;
     
    +import emissary.config.Configurator;
    +import emissary.config.ServiceConfigGuide;
     import emissary.test.core.junit5.UnitTest;
     
     import jakarta.annotation.Nullable;
    @@ -603,6 +605,101 @@ void testCleanPlaceName() {
             assertEquals("PLACE-NAME-1", Executrix.cleanPlaceName("PLACE-NAME-1"));
         }
     
    +    // -----------------------------------------------------------------------
    +    // cleanFileEnding – covers INVALID_FILE_ENDING_CHARS and cleanFileEnding()
    +    // -----------------------------------------------------------------------
    +
    +    @Test
    +    void testCleanFileEndingAllowedCharsPassThrough() {
    +        // Normal extensions must survive unchanged
    +        assertEquals(".out", Executrix.cleanFileEnding(".out"));
    +        assertEquals(".txt", Executrix.cleanFileEnding(".txt"));
    +        assertEquals("", Executrix.cleanFileEnding(""));
    +        assertEquals(".out-1_a", Executrix.cleanFileEnding(".out-1_a"));
    +        assertEquals("ABC123._-", Executrix.cleanFileEnding("ABC123._-"));
    +    }
    +
    +    @Test
    +    void testCleanFileEndingShellMetacharacters() {
    +        // Every shell-special character must be individually replaced with '_'
    +        assertEquals("_", Executrix.cleanFileEnding("`"));
    +        assertEquals("_", Executrix.cleanFileEnding("$"));
    +        assertEquals("_", Executrix.cleanFileEnding(";"));
    +        assertEquals("_", Executrix.cleanFileEnding("&"));
    +        assertEquals("_", Executrix.cleanFileEnding("|"));
    +        assertEquals("_", Executrix.cleanFileEnding(">"));
    +        assertEquals("_", Executrix.cleanFileEnding("<"));
    +        assertEquals("_", Executrix.cleanFileEnding("!"));
    +        assertEquals("_", Executrix.cleanFileEnding(" "));
    +        assertEquals("_", Executrix.cleanFileEnding("("));
    +        assertEquals("_", Executrix.cleanFileEnding(")"));
    +        assertEquals("_", Executrix.cleanFileEnding("'"));
    +        assertEquals("_", Executrix.cleanFileEnding("\""));
    +        assertEquals("_", Executrix.cleanFileEnding("\\"));
    +        assertEquals("_", Executrix.cleanFileEnding("/"));
    +        assertEquals("_", Executrix.cleanFileEnding("\n"));
    +        assertEquals("_", Executrix.cleanFileEnding("\t"));
    +    }
    +
    +    @Test
    +    void testCleanFileEndingDoesNotStripHyphenOrUnderscore() {
    +        // Hyphen and underscore are safe in file extensions and must not be replaced
    +        assertEquals(".tar.gz", Executrix.cleanFileEnding(".tar.gz"));
    +        assertEquals(".out-v2", Executrix.cleanFileEnding(".out-v2"));
    +        assertEquals(".out_v2", Executrix.cleanFileEnding(".out_v2"));
    +    }
    +
    +    // -----------------------------------------------------------------------
    +    // setInFileEnding / setOutFileEnding – sanitization enforced by setters
    +    // -----------------------------------------------------------------------
    +
    +    @Test
    +    void testSetFileEndingsNormalValuesUnchanged() {
    +        // Legitimate extensions must not be altered by the setters
    +        e.setInFileEnding(".in");
    +        assertEquals(".in", e.getInFileEnding());
    +
    +        e.setOutFileEnding(".out");
    +        assertEquals(".out", e.getOutFileEnding());
    +    }
    +
    +    @Test
    +    void testSetFileEndingsSanitizeInjectionPayloads() {
    +        e.setInFileEnding("`id`");
    +        assertEquals("_id_", e.getInFileEnding());
    +
    +        e.setOutFileEnding(";rm -rf /");
    +        assertEquals("_rm_-rf__", e.getOutFileEnding());
    +    }
    +
    +    // -----------------------------------------------------------------------
    +    // configure() path – sanitization applied when reading from Configurator
    +    // -----------------------------------------------------------------------
    +
    +    @Test
    +    void testConfigureFileEndingsSanitizedFromConfig() {
    +        // Injection payloads in config must be neutralized at construction time
    +        final Configurator cfg = new ServiceConfigGuide();
    +        cfg.addEntry("IN_FILE_ENDING", "`id > /tmp/pwned.txt`");
    +        cfg.addEntry("OUT_FILE_ENDING", "$(whoami)");
    +
    +        final Executrix ex = new Executrix(cfg);
    +        assertEquals("_id____tmp_pwned.txt_", ex.getInFileEnding());
    +        assertEquals("__whoami_", ex.getOutFileEnding());
    +    }
    +
    +    @Test
    +    void testConfigureNormalFileEndingsUnchanged() {
    +        // Normal extensions must pass through configure() without modification
    +        final Configurator cfg = new ServiceConfigGuide();
    +        cfg.addEntry("IN_FILE_ENDING", ".xml");
    +        cfg.addEntry("OUT_FILE_ENDING", ".json");
    +
    +        final Executrix ex = new Executrix(cfg);
    +        assertEquals(".xml", ex.getInFileEnding());
    +        assertEquals(".json", ex.getOutFileEnding());
    +    }
    +
         private static void readAndNuke(final String name) throws IOException {
             final File f = new File(name);
             assertTrue(f.exists(), "File " + name + " must exist");
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.