Disallowed Tag Library

ID

html.disallowed_taglibs

Severity

critical

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Code Smell

Language

Html

Tags

best-practice, code-style

Description

Reports a page that pulls in a tag library the project has decided its views must not use.

Both spellings of a library declaration are read, because the two view technologies that share the HTML file disagree about it:

  • JSP writes a directive — <%@ taglib uri="…" prefix="…" %>.

  • Facelets writes an XML namespace attribute on any element — xmlns:sql="…".

A JSP comment declares nothing, so <%-- taglib uri="…" --%> is not reported, and neither is a page or include directive, nor an ordinary attribute that merely happens to hold a URI.

disallowedUris ships denying the JSTL sql library under every spelling it has shipped with — jakarta.tags.sql, http://java.sun.com/jsp/jstl/sql, http://xmlns.jcp.org/jsp/jstl/sql, http://java.sun.com/jstl/sql and http://java.sun.com/jstl/sql_rt — because querying the database from the markup is a defect in every project rather than a matter of local policy.

Which further libraries are unacceptable is a project decision: add them to disallowedUris, or set the list empty to turn the URI check off. disallowedPrefixes stays empty by default, since an alias is a local naming convention no default can know.

Rationale

A tag library is a dependency of the view, and it is a dependency declared where dependencies are hardest to see — inside the markup, rather than in the build file the rest of the project’s dependencies live in. Some libraries carry capabilities a presentation layer has no business holding.

The standing example is the JSTL sql library. It lets a page open a connection and run a query from inside the markup:

<%@ taglib uri="jakarta.tags.sql" prefix="sql" %>
<sql:query var="rows" dataSource="jdbc/reports">
  SELECT * FROM orders WHERE customer = '${param.customer}'
</sql:query>

Every problem with that is structural rather than stylistic. Data access has moved into the view, so it is outside the transaction, connection-pool and audit boundaries the application maintains everywhere else. The query is assembled by string substitution from a request parameter, which is SQL injection with no layer left in which to parameterise it. And the query is invisible to anyone reading the service layer, so the page’s data dependencies do not appear in any inventory of them.

The same reasoning covers a project’s own libraries. Once a shared component library is superseded, a new page must not reach for the old one; a library reserved for the admin console must not be declared by a customer-facing page; a library whose tags emit unescaped markup must not be available to a view that renders user content. In each case the decision is already made and written down somewhere, and this rule is what makes it hold at the point of use.

The Facelets spelling matters just as much as the JSP one, and is the one a modern codebase actually contains:

<h:panelGroup xmlns:sql="jakarta.tags.sql">

Remediation

Remove the declaration and move the work it enabled into the layer that owns it. For the sql example, the query belongs in a repository or service, and the page receives the finished rows through the model:

<h:dataTable value="#{reportBean.orders}" var="order">
  <h:column>#{order.reference}</h:column>
</h:dataTable>

For a superseded component library, migrate the page to the replacement and drop the old prefix. For a library that is legitimate in one part of the application and not another, the deny-list is per-configuration, so the restriction can be scoped to the views it applies to rather than applied project-wide.

Configuration

Property Default Description

disallowedUris

the JSTL sql library, in its five spellings

URIs of the tag libraries the project’s views must not declare. Matched in full and case-insensitively, against a JSP taglib directive’s uri and against an xmlns: namespace value. This is the list to prefer: the URI is the library’s identity. Replacing the list replaces the default, so a project that adds its own denied libraries keeps the JSTL sql entries in it.

disallowedPrefixes

[]

Local prefixes the project’s views must not bind, for a project that has standardised its aliases. Matched in full and case-insensitively. A prefix is only a local alias, so a deny-list built on prefixes is evaded by renaming; use it as a convenience, not as the primary list.

With both lists empty the rule reports nothing.

© 2026 Xygeni. All rights reserved