What are the best tools for running security analysis on Clojure codebases? Main thing I found for SAST is clj-watson, but the last update was 4 years ago. I'm interested in both libraries and SaaS offerings I could use.
Four years? You're looking at the wrong thing. It was updated last month with a major new release: https://github.com/clj-holmes/clj-watson
clj-watson is what we use at work, which is why I'm one of the maintainers 🙂
@seancorfield my mistake, I meant clj-holmes. Looking for SAST.
Ah, source analysis. I don't know of anything for Clojure (and wouldn't recommend clj-holmes, TBH -- we've found it problematic to use, even with clj-watson maintenance).
Mythic will send you 400 issues no problem
AOT compilation in libraries: Is there ever a case for it? I know the stdlib is aot'd and direct linked, but other than that (bonus question: How feasible is it to have the stdlib not aot'd. I have a reason for it i think)
A big one is environments that can't compile code at runtime like graalvm and android (is that still the case there?)
but that isn't a reason to aot libraries, that is a reason to aot programs
misread, shame on me
the big issue with aot'iong libraries is transitive compilation (in a coupld of ways), in general because clojure itself doesn't depend on other libraries (including clojure itself as a library) it doesn't have those issues when aot compiled
so it is aot compiled so it loads faster
people do distribute aot compiled clojure libraries though (I think the datomic client stuff may be the big example of this?)
the transitive compilation problems come in two forms basically: 1. dependencies are also aot compiled and the code is generate for those so if you are not careful you will end up distributing your dependencies code in your library build which causes problems for consumers understanding what versions of code they are running 2. your dependencies are part of your compilation environment, and can influence how the code is compiled, and result in the code being compiled in a fragile way that won't work with other versions. (this is the more subtle issue that people run afoul of when aot'ing their library)
I'll give a little context on why I am asking this, in case its helpful:
I am working on a maven central replacement where a constraint is that artifacts must be JVM modules. I have reasons for that, but its a whole tangent.
If a clojure library is packaged up with a dummy module-info it works perfectly fine. It is just resources.
But when they are AOT'd I encounter two issues
1. There is something going on where classes get defined twice (genuinely don't have a root cause yet, but while clojure works, as soon as I pull in clojure.spec something breaks)
2. Namespaces are translated like so
(ns -> class
(ns demo.stuff) -> class demo.stuff
Which bumps into the one relevant restriction of modules: a given package can only be owned by one module. This is fine until it isn't
(one wacko direction i've thought of is changing clojure so that the translation is
(ns -> class demo.app.$
(ns demo.stuff) -> class demo.stuff.$
but that gets annoying instantly)
we do build and publish a "slim" classifier source-only version of clojure too, btw
I will try the -slim. I did not know about that
(module reference joke)
RIP Joe 😢
Fascinating thread
but going to try out the slim artifact with my scheme. If it works i'll get excited. I don't give much thought to kotlin and scala in my schemes, but I want to make sure clojure works
Syntax error (ClassNotFoundException) compiling at (clojure/repl.clj:285:4).
sun.misc.Signal
Using the slim artifact on a newer JDKthat class is still available in the most recent JDKs and afaik they do not have a plan to remove it yet
it is due to enabling modules
$ clj
Clojure 1.12.5
user=> (System/getProperty "java.version")
"26.0.1"
user=> (require '[clojure.repl :as repl])
nil
user=> (repl/set-break-handler! #(println "hi" %))
#object[clojure.repl.proxy$java.lang.Object$SignalHandler$d8c00ec7 0x5e39850 "clojure.repl.proxy$java.lang.Object$SignalHandler$d8c00ec7@5e39850"]
user=> hi -- caught signal SIGINT
user=>
^^ test on Java 26 for exampleIt is due to removing unused modules - I am adding a synthetic module descriptor to org.clojure
so i didn't include jdk.unsupported initially
Does AOT make sense if you don't want to deliver the source code?
jlink --add-modules ALL-MODULE-PATH -p jars --output jdk --launcher clojure=org.clojure/clojure.mainright now the issue I identified is that clojure (when it is a module) won't find resources in other modules...
jdk/bin/clojure
Clojure 1.12.5
user=> (require '[ring.util.response])
Execution error (FileNotFoundException) at user/eval8844 (REPL:1).
Could not locate ring/util/response__init.class, ring/util/response.clj or ring/util/response.cljc on classpath.
user=> (require '[clojure.core.specs.alpha])
nil
user=>
Executable jar files. The main function must be AOT.
> Executable jar files. The main function must be AOT That makes sense for an AOT compiled app. Less so for libraries unless they want to play double duty
emccue@Ethans-MacBook-Pro linkedclj % jdk/bin/clojure
Clojure 1.12.5
user=> (java.lang.ModuleLayer/boot)
#object[java.lang.ModuleLayer 0x71ce19ee "org.clojure.core.specs.alpha, java.logging, java.xml, java.desktop, java.transaction.xa, java.datatransfer, jdk.unsupported, org.clojure.spec.alpha, java.prefs, , java.base, org.clojure, java.sql"]
user=> ^D
emccue@Ethans-MacBook-Pro linkedclj % jdk/bin/java --list-modules
java.base@25.0.1
java.datatransfer@25.0.1
java.desktop@25.0.1
java.logging@25.0.1
java.prefs@25.0.1
java.sql@25.0.1
java.transaction.xa@25.0.1
java.xml@25.0.1
jdk.internal.vm.ci@25.0.1
jdk.unsupported@25.0.1
org.clojure@1.12.5 open
org.clojure.core.specs.alpha@0.5.81 open
org.clojure.spec.alpha@0.6.249 open
ring.core@1.15.5 open
So interestingly ModuleLayer.boot() doesn't have all the modules that come with the JDK. I need to look into clojure.main--add-modules=ALL-SYSTEM
And it works