crimes 0.31.0: JSP, LESS and CSS, and scans that lead with what matters
JSON schema remains 0.8.0. Fingerprints of existing findings are unchanged. 0.31 reads server-rendered views and stylesheets — JSP pages, fragments, tag files and tag library descriptors, LESS and plain CSS — and follows what one template or stylesheet hides: the pages that reach a tag through a prefix declared somewhere else, and the partials that use a variables file they never import. It also reorders full scans on JVM and mobile code, and adds public Java, Kotlin, Swift, JSP and LESS repositories to the eval corpus.
Upgrading
Section titled “Upgrading”Nothing you have recorded stops matching. What you may notice:
- New files are discovered by default:
.jsp,.jspf,.tag,.tld,.lessand.css. They appear incoverage.files_by_languageasjsp,lessandcss, andcoverage.packs_loadedgainslanguage-jspandlanguage-less.Finding.packcan carry either value; consumers that switch on it should tolerate them, as for earlier packs. - Two new finding types:
dangling_template_reference(detectordangling_template_reference.jsp) andoverridden_variable(detectoroverridden_variable.less). - Templates and stylesheets carry no test gap, as documentation does,
and
contextgives no test-gap clue for them. Their blast radius is scored. - A new
large_filethreshold,thresholds.largeFile.stylesheet(default 600 lines), for.lessand.css. - Blast radius changed on cyclic graphs (0.30.2): reach no longer walks round a file’s own import cycle. On Kotlin, where most files share one cycle, scores spread out instead of all sitting near 0.77.
contextnames the relation for template and stylesheet edges.related_files[].reasonsays “uses @brand, defined here”, “calls this tag as <x:card>” or “includes this file” where the edge is not an import.- Test-quality findings on test files (
weak_test_signal,mock_saturation) now take the blast radius of the code under test, and say which file in their evidence.
What changes
Section titled “What changes”JSP. A lexical scanner, not a grammar: directives, actions, custom tags,
EL and scriptlets are read as facts, never guessed. Pages link to what they
include (<%@ include %>, <jsp:include>, <c:import>, and web.xml
jsp-property-group preludes and codas), to the tag files their tags
resolve to — taglib prefixes bind across included fragments, as in a JSP
translation unit — to tag library descriptors, and to the Java classes they
name in imports, beans, attribute types and scriptlets. TLDs link to their
tag, function and listener classes. So a Java class used only from
templates gets a real blast radius, and crimes context on a tag file
lists the pages that call it, which a search for the file name does not
find.
dangling_template_reference.jsp reports what JSP checks only on the first
request: an include, tag file or .tld that does not exist while its
directory does, and a tag call that omits a required attribute or passes
one the tag does not declare. A missing directory is read as a resource a
library supplies and never reported.
LESS and CSS. @import edges, and edges from a partial to the files
defining the variables and mixins it uses: a partial compiled inside a
bundle uses names the bundle’s root imported, never importing them itself.
Plain CSS is read as the LESS subset it is, and JavaScript imports of
stylesheets resolve (without counting as code fan-out).
overridden_variable.less simulates each bundle’s import order. LESS
evaluates variables lazily and the last definition wins everywhere, so a
definition a later import redefines does nothing; it is reported when that
holds in every bundle compiling it. A base value one bundle overrides and
another uses — the theme pattern — is not.
Stylesheet calibration. commented_out_code does not read stylesheets
(commented-out declarations are inert). large_file gives them a 600-line
budget and skips a stylesheet library copied in under its /*! name vX.Y.Z
banner.
Scan ranking on JVM and mobile code (0.30.2, 0.31.0).
- Blast radius walks the condensation of the import graph: members of a file’s own cycle count only when they import it directly.
commented_out_coderamps to its cap over 16 lines rather than 6.- Each further finding of the same claim in one file sorts at 0.75ⁿ of its rank score, so one file’s repeats give way to other files’.
- Test-quality findings are scored by the code under test.
Precision on public Java, Kotlin and Swift (0.30.1). Snapshot test
baselines and .appiconset contents are exempt from the image detectors;
Kotlin generic, screenshot and Robolectric assertions count; Swift tests on
a project’s own XCTestCase subclass are recognised; catch (… ignored2)
reads as deliberate.
Security. The scanners are linear on hostile input. Reviews of this
work found and fixed two super-linear paths in the JSP scanner and
descriptor readers (an EL expression left open in an attribute value
scanned quadratically; descriptor patterns could backtrack) and an
exponential path in the LESS bundle simulation ((multiple) imports).
Hostile-input tests pin each one.
Measured
Section titled “Measured”On the private estate behind 0.30 (a Java web application with 1,089
templates and 948 stylesheets), against a 0.30.2 build: every tagdir tag
call (3,828) resolves; no reference is falsely reported dangling; 258
existing findings gain blast radius, mostly Java classes used from
templates; 9 of the top 10 stay in the top 10. Findings 1,590 → 1,682,
every one of the 92 additions on a template or stylesheet. overridden_variable.less
reports four definitions there, all real: two redundant copies and two
overrides that a later import undoes (an asset path and a feature flag).
On public repositories: Apache Roller 444 → 451 findings (one false
orphaned_destination on a script a JSP includes removed);
spring-framework-petclinic unchanged; Bootstrap 3.4.1 37 → 41. At 0.30.1
the precision fixes took Now in Android from 57 to 33 findings and
Kickstarter’s iOS app from 713 to 366.
Evals. A full live round at 0.31.0 completed 134/134 cells (67
scenarios, Claude and Codex). On the 60 scenarios shared with the last
round (0.29.9, re-scored with the same scorer) Claude holds at 0.86 and
Codex rises from 0.72 to 0.75; on the seven new Java, Kotlin, Swift, JSP and
LESS scenarios both score 0.96. Two scorer faults found on the way were
corrected and re-scored: file paths with the new packs’ extensions were not
recognised, and paths an agent linked absolutely into its disposable
workspace did not count (12 Codex cells). Ranking on the labelled
scenarios: mean_ndcg_deep 0.4041, against 0.4059 at 0.30.2 on a deep
set that has since grown from 37 to 38 scenarios.
Performance. On the estate’s web application the scan now parses about 2,000 more files: CPU time rose from 12.8–14.3 s to 15.0–15.7 s across two runs each, and peak memory was unchanged at 410–465 MB. The JSP and LESS packs are pure TypeScript and add no WebAssembly.
Still unsettled
Section titled “Still unsettled”- Clock reads lead the Java backends’ top tens. Discounting recorded readings in files that also compare would move them down, and was measured and rejected: it lowers the labelled metric on exactly the case it should keep, and the readings sit in each backend’s most central files, deciding expiry and age checks.
- A large file used everywhere ranks low. On Bootstrap,
large_fileonvariables.less(53 users) ranks 34th of 40, because structural types are scaled down. Whether wide use should lift a structural finding is undecided. - TLD-declared tag contracts are checked but no measured repository exercised them.
- Large repositories: a full n8n scan still takes about 27 minutes.
- Java 21 type patterns still do not parse with the current tree-sitter-java.
Upgrade by updating the npm package normally. See language packs and calibration follow-ups.