VYPR
Medium severity4.8NVD Advisory· Published Apr 7, 2026· Updated Apr 27, 2026

CVE-2026-35571

CVE-2026-35571

Description

Emissary is a P2P based data-driven workflow engine. Prior to 8.39.0, Mustache navigation templates interpolated configuration-controlled link values directly into href attributes without URL scheme validation. An administrator who could modify the navItems configuration could inject javascript: URIs, enabling stored cross-site scripting (XSS) against other authenticated users viewing the Emissary web interface. This vulnerability is fixed in 8.39.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

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

Affected products

1
  • cpe:2.3:a:nsa:emissary:*:*:*:*:*:*:*:*
    Range: <=8.38.0

Patches

1
e2078417464b

ui :: validate nav links (#1293)

3 files changed · +63 3
  • src/main/java/emissary/server/mvc/NavAction.java+19 1 modified
    @@ -8,16 +8,21 @@
     import jakarta.ws.rs.Produces;
     import jakarta.ws.rs.core.MediaType;
     import org.glassfish.jersey.server.mvc.Template;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
     
     import java.io.IOException;
     import java.util.List;
     import java.util.Map;
    +import java.util.regex.Pattern;
     import java.util.stream.Collectors;
     
     @Path("")
     // context is emissary
     public class NavAction {
     
    +    private static final Logger logger = LoggerFactory.getLogger(NavAction.class);
    +
         EmissaryNav nav;
     
         public NavAction() throws IOException {
    @@ -39,6 +44,8 @@ public EmissaryNav nav() {
     
         public static class EmissaryNav {
     
    +        private static final Pattern VALID_LINK = Pattern.compile("^(https?:/)?/.*");
    +
             String appName;
             String appVersion;
             List<NavItem> navItems;
    @@ -77,7 +84,18 @@ public void setNavButtons(Map<String, String> navButtons) {
             }
     
             protected static List<NavItem> convert(Map<String, String> map) {
    -            return map.entrySet().stream().map(e -> new NavItem(e.getKey(), e.getValue())).collect(Collectors.toList());
    +            return map.entrySet().stream()
    +                    .filter(e -> isValidLink(e.getValue()))
    +                    .map(e -> new NavItem(e.getKey(), e.getValue()))
    +                    .collect(Collectors.toList());
    +        }
    +
    +        private static boolean isValidLink(String link) {
    +            if (!VALID_LINK.matcher(link).matches()) {
    +                logger.warn("Skipping invalid navigation link '{}'", link);
    +                return false;
    +            }
    +            return true;
             }
     
             public static class NavItem {
    
  • src/main/resources/templates/nav.mustache+2 2 modified
    @@ -8,12 +8,12 @@
                 <ul class="navbar-nav me-auto">
                     {{#navItems}}
                         <li class="nav-item">
    -                        <a class="nav-link" href="{{link}}">{{display}}</a>
    +                        <a class="nav-link" href="{{link}}" rel="noopener noreferrer">{{display}}</a>
                         </li>
                     {{/navItems}}
                     {{#navButtons}}
                         <li class="nav-item">
    -                        <a href="{{link}}" class="btn btn-danger navbar-btn" style="margin: 2px 2px 0px 0px;" onclick="return confirm('Are you sure?')">{{display}}</a>
    +                        <a href="{{link}}" rel="noopener noreferrer" class="btn btn-danger navbar-btn" style="margin: 2px 2px 0px 0px;" onclick="return confirm('Are you sure?')">{{display}}</a>
                         </li>
                     {{/navButtons}}
                 </ul>
    
  • src/test/java/emissary/server/mvc/NavActionTest.java+42 0 added
    @@ -0,0 +1,42 @@
    +package emissary.server.mvc;
    +
    +import emissary.server.mvc.NavAction.EmissaryNav;
    +
    +import org.apache.commons.collections4.CollectionUtils;
    +import org.junit.jupiter.api.Test;
    +
    +import java.util.LinkedHashMap;
    +import java.util.Map;
    +
    +import static org.junit.jupiter.api.Assertions.assertEquals;
    +import static org.junit.jupiter.api.Assertions.assertTrue;
    +
    +class NavActionTest {
    +
    +    @Test
    +    void navItems() {
    +
    +        var navItems = new LinkedHashMap<String, String>();
    +        navItems.put("Item1", "/path/to/resource");
    +        navItems.put("Item2", "http://testing1.com");
    +        navItems.put("Item3", "https://testing2.com");
    +        navItems.put("Item4", "javascript:alert(document.cookie)");
    +        navItems.put("Item5", "ftp://testing");
    +
    +        EmissaryNav nav = new EmissaryNav();
    +
    +        // insert empty map
    +        nav.setNavItems(Map.of());
    +        assertTrue(CollectionUtils.isEmpty(nav.getNavItems()));
    +
    +        nav.setNavItems(navItems);
    +        assertEquals(3, nav.getNavItems().size());
    +        assertEquals("Item1", nav.getNavItems().get(0).getDisplay());
    +        assertEquals("/path/to/resource", nav.getNavItems().get(0).getLink());
    +        assertEquals("Item2", nav.getNavItems().get(1).getDisplay());
    +        assertEquals("http://testing1.com", nav.getNavItems().get(1).getLink());
    +        assertEquals("Item3", nav.getNavItems().get(2).getDisplay());
    +        assertEquals("https://testing2.com", nav.getNavItems().get(2).getLink());
    +    }
    +
    +}
    

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

5

News mentions

0

No linked articles in our index yet.