Welcome! Please see the About page for a little more info on how this works.

+3 votes
in Syntax and reader by
retagged by

I thought Clojure automatically loads namespaces for vars used to read tagged literals in the source code, but apparently it isn't.

Given a library that defines data_readers.clj with following content:

{foo/bar my.library.foo/bar
 foo/baz my.library/baz}

An attempt to read #foo/bar [1 2 3] will result in this exception:

Syntax error reading source at (REPL:1:69).
Attempting to call unbound fn: #'my.library.foo/bar

To avoid this exception you need to require the library namespace before using reader tags.

It would be more convenient to be able to use library reader tags just by adding it to the classpath without explicit require step.

I don't know why it's made this way...

If the reasoning is clojure.core startup time, perhaps the actual load of the var ns might happen at the first tagged literal read time?
If the reasoning is security implications of unexpected code execution, maybe the behavior of requiring data reader namespaces could be configured with *read-eval*?

2 Answers

0 votes
A note on security implications: my understanding is that a threat here is a malicious package on the classpath that is activated when vulnerable system does read-string on user input supplied by the attacker. I think while this is insecure, a presence of a malicious package on the classpath is already enough of a security vulnerability — it can provide it's own `clojure.core` ns that will be loaded from the classpath instead of the one provided by `org.clojure/clojure` artifact.

So there is really no point for attacker to use reader tags for an attack when ending up on the classpath is enough.

So maybe reader tag nses should always be required, and instead of using `*read-eval*` to control this loading it's enough to advice users to perform read with `*data-readers*` bound to `default-data-readers` that is already there...
0 votes

It would be great if this behaviour changed or could be configured.

Currently it's pretty hard to use dev time tools like hasp or debux, since you have to either require them everywhere or use workarounds like user.clj inject to get the require.