CVE-2018-0046 (Other, published 2018-10-10T18:29Z)
Description: value: A reflected cross-site scripting vulnerability in OpenNMS included with Juniper Networks Junos Space may allow the stealing of sensitive information or session credentials from Junos Space administrators or perform administrative actions. This issue affects Juniper Networks Junos Space versions prior to 18.2R1.
Severity: MEDIUM (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/OpenNMS/opennms
Files changed:
opennms-webapp/src/main/webapp/asset/modify.jsp
opennms-webapp/src/main/webapp/asset/nodelist.jsp
Commit message: NMS-9065: Fixed the Asset pages JSPs to handle ACL filtering for release-19.0.0


Vulnerable code:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div class="container-fluid" ng-app="onms-assets" ng-controller="NodeAssetsCtrl" ng-init="init(${nodeId})">
  <div growl></div>
  <h4>
    Node: <strong><a href="element/node.jsp?node=${nodeId}">{{ nodeLabel }}</a></strong>
  </h4>
  <p>
    Last modified by {{ master['lastModifiedBy'] }} at {{ master['lastModifiedDate'] | date }}
  </p>
  <form name="assetForm" novalidate>
    <div class="row" ng-repeat="row in config.rows">
      <div ng-class="col.class" ng-repeat="col in row.columns">
        <div class="panel panel-default" ng-repeat="panel in col.panels">
          <div class="panel-heading">
            <h3 class="panel-title">{{ panel.title }}</h3>
          </div>
          <div class="panel-body">
            <div class="form-horizontal" ng-repeat="field in panel.fields"> 
              <div class="form-group" ng-class="{ 'has-error': assetForm[field.model].$invalid && !assetForm[field.model].$pristine, 'has-warning': assetForm[field.model].$dirty }">
                <label class="control-label col-md-3" for="{{ field.model }}" uib-tooltip="{{ field.tooltip  }}">{{ field.label }}</label>
                <div class="col-md-9">
                  <%-- Static/ReadOnly fields --%>
                  <p class="form-control-static" ng-if="field.type=='static'">{{ asset[field.model] }}</p>
                  <%-- Standard fields with typeahead suggestions --%>
                  <input type="text" class="form-control" id="{{ field.model }}" name="{{ field.model }}" ng-model="asset[field.model]" ng-if="field.type=='text'"
                    typeahead-editable="true" typeahead-min-length="0" ng-pattern="field.pattern"
                    uib-typeahead="suggestion for suggestion in getSuggestions(field.model) | filter:$viewValue"></input>
                  <%-- Password fields --%>
                  <input type="password" class="form-control" ng-model="asset[field.model]" ng-if="field.type=='password'"></input>
                  <%-- Textarea fields --%>
                  <textarea class="form-control" style="height: 20em;" ng-model="asset[field.model]" ng-if="field.type=='textarea'"></textarea>
                  <%-- Date fields with Popup Picker --%>
                  <p class="input-group" ng-if="field.type=='date'">
                    <input type="date" class="form-control" uib-datepicker-popup="{{ dateFormat }}" is-open="field.open" ng-model="asset[field.model]" placeholder="Specify date using this format: {{ dateFormat }}" />
                    <span class="input-group-btn">
                      <button type="button" class="btn btn-default" ng-click="field.open=true"><i class="glyphicon glyphicon-calendar"></i></button>
                    </span>
                  </p>
                  <%-- List/Select fields --%>
                  <select class="form-control" ng-model="asset[field.model]" ng-if="field.type=='select'">
                    <option ng-repeat="value in field.options">{{value}}</option>
                  </select>
            </div> 
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-md-6">
        <div class="btn-group">
          <button type="button" class="btn btn-default" ng-click="save()" id="save-asset" ng-disabled="assetForm.$invalid">Save Asset Record&nbsp;&nbsp;&nbsp;
            <span class="glyphicon glyphicon-save"><span>
          </button>
          <button type="button" class="btn btn-default" ng-click="reset()" id="reset-asset">Reset&nbsp;&nbsp;&nbsp;
            <span class="glyphicon glyphicon-refresh"><span>
          </button>
        </div>
    </div>
  </form>

</div>
	contentType="text/html"
	session="true"
	import="org.opennms.web.asset.*,
		org.opennms.web.servlet.MissingParameterException 
	"
%>

        <% for( int i=0; i < assets.length; i++ ) { %>

Fixed code:
<%@ page contentType="text/html;charset=UTF-8" language="java" import="org.opennms.web.springframework.security.AclUtils"%>
<%
  AclUtils.NodeAccessChecker accessChecker = AclUtils.getNodeAccessChecker(getServletContext());
  Integer nodeId = null;
  try {
    nodeId = Integer.valueOf(request.getParameter("node"));
  } catch (NumberFormatException e) {
%>
    <h2>Error parsing node parameter.</h2>
<%
  }
  if (nodeId != null) {
    if (accessChecker.isNodeAccessible(nodeId)) {
%>
      <div class="container-fluid" ng-app="onms-assets" ng-controller="NodeAssetsCtrl" ng-init="init(${nodeId})">

        <div growl></div>

        <h4>
          Node: <strong><a href="element/node.jsp?node=${nodeId}">{{ nodeLabel }}</a></strong>
        </h4>
        <p>
          Last modified by {{ master['lastModifiedBy'] }} at {{ master['lastModifiedDate'] | date }}
        </p>

        <form name="assetForm" novalidate>
          <div class="row" ng-repeat="row in config.rows">
            <div ng-class="col.class" ng-repeat="col in row.columns">
              <div class="panel panel-default" ng-repeat="panel in col.panels">
                <div class="panel-heading">
                  <h3 class="panel-title">{{ panel.title }}</h3>
                </div>
                <div class="panel-body">
                  <div class="form-horizontal" ng-repeat="field in panel.fields">
                    <div class="form-group" ng-class="{ 'has-error': assetForm[field.model].$invalid && !assetForm[field.model].$pristine, 'has-warning': assetForm[field.model].$dirty }">
                      <label class="control-label col-md-3" for="{{ field.model }}" uib-tooltip="{{ field.tooltip  }}">{{ field.label }}</label>
                      <div class="col-md-9">
                        <%-- Static/ReadOnly fields --%>
                        <p class="form-control-static" ng-if="field.type=='static'">{{ asset[field.model] }}</p>
                        <%-- Standard fields with typeahead suggestions --%>
                        <input type="text" class="form-control" id="{{ field.model }}" name="{{ field.model }}" ng-model="asset[field.model]" ng-if="field.type=='text'"
                          typeahead-editable="true" typeahead-min-length="0" ng-pattern="field.pattern"
                          uib-typeahead="suggestion for suggestion in getSuggestions(field.model) | filter:$viewValue"></input>
                        <%-- Password fields --%>
                        <input type="password" class="form-control" ng-model="asset[field.model]" ng-if="field.type=='password'"></input>
                        <%-- Textarea fields --%>
                        <textarea class="form-control" style="height: 20em;" ng-model="asset[field.model]" ng-if="field.type=='textarea'"></textarea>
                        <%-- Date fields with Popup Picker --%>
                        <p class="input-group" ng-if="field.type=='date'">
                          <input type="date" class="form-control" uib-datepicker-popup="{{ dateFormat }}" is-open="field.open" ng-model="asset[field.model]" placeholder="Specify date using this format: {{ dateFormat }}" />
                          <span class="input-group-btn">
                            <button type="button" class="btn btn-default" ng-click="field.open=true"><i class="glyphicon glyphicon-calendar"></i></button>
                          </span>
                        </p>
                        <%-- List/Select fields --%>
                        <select class="form-control" ng-model="asset[field.model]" ng-if="field.type=='select'">
                          <option ng-repeat="value in field.options">{{value}}</option>
                        </select>
                      </div>
                    </div>
                  </div>
            </div>
          <div class="row">
            <div class="col-md-6">
              <div class="btn-group">
                <button type="button" class="btn btn-default" ng-click="save()" id="save-asset" ng-disabled="assetForm.$invalid">Save Asset Record&nbsp;&nbsp;&nbsp;
                  <span class="glyphicon glyphicon-save"><span>
                </button>
                <button type="button" class="btn btn-default" ng-click="reset()" id="reset-asset">Reset&nbsp;&nbsp;&nbsp;
                  <span class="glyphicon glyphicon-refresh"><span>
                </button>
              </div>
            </div>
          </div>
        </form>
<%
    } else {
%>
      <h2>Access denied.</h2>
<%
    }
  }
%>
        contentType="text/html"
        session="true"
        import="org.opennms.web.asset.*,
		    org.opennms.web.servlet.MissingParameterException,
            org.opennms.web.springframework.security.AclUtils"%>

    AclUtils.NodeAccessChecker accessChecker = AclUtils.getNodeAccessChecker(getServletContext());
        <% for( int i=0; i < assets.length; i++ ) {
            if (!accessChecker.isNodeAccessible(assets[i].nodeId)) {
                continue;
            }
        %>