Share your thoughts in the 2025 State of Clojure Survey!

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

+2 votes
ago in Syntax and reader by
edited ago by

clojure.edn allow leading zeros ending up reading such number as octal representation. The problem is that it is also allowed to have indefinite amount of leading zeros for octals:

000000000000042  ;; => 34

and when there is a suffix "M" that forces floating point representation the same string become float 42 stripping all leading zeros.

000000000000042M ;; => 42M

At the same time EDN specification forbids integers with leading zeros at all even for floats.
I found a ticket about leading zeros in data.json but I think it should be expanded to cover clojure as well.

In the same scope but origin is in the EDN spec:
float specification allows leading zeroes in exponent and only zeros in fractional parts:

0.00000000...00
0.0e0001

probably because of that clojure.edn read such numbers without error too.

ago by
But why is that a problem? Clojure syntax is a superset of EDN - EDN deliberately does not support everything that Clojure allows. Whether 1+ leading zeroes is in that deliberate set I do not know, but it doesn't seem like it's a problem by itself anyway. The `data.json` situation is different because it's about JSON syntax, not Clojure syntax.
ago by
1. it is inconsistent. Other integer forms (except octal) forbids leading zeros.
2. early termination in such cases is important for safety reasons.
ago by
1. Then the argument could be made that things should be made consistent, instead of being prohibited. But any change here would be breaking: https://github.com/epiccastle/spire/blob/master/src/clj/spire/nio.clj#L66

2. I don't understand. How is allowing numbers like `0...01` not safe, specifically?
ago by
edited ago by
2. Because edn is a data transfer format. You can slow down a service til DOS supplying looooong sequence of zeros with a number at the end. It will be required from the service to parse it completely, hiding from the consumer root cause of slownes.

btw, I just realise that I was a bit too deep in intricacies of EDN specification and phrased this ask the wrong way :) It is solely about clojure.edn, not clojure core.
ago by
"It is solely about clojure.edn, not clojure core." - ah, alright, it makes more sense that way.

Regarding safety - I still don't see it as a strong argument. Just as you can have some "0...01" in some EDN file, you can also have "1...1" in a file. Or just a random insanely large string. If you ingest random third-party files, you are by default susceptible to DoS attacks unless every single aspect of data ingestion is guarded.
ago by
True, but in case there is a BigInteger it could be caught at the border via validation, unlike an enormously long representation of 0 or any other small integer.

    ❯ clj                           
    Clojure 1.12.3
    user=> (def long-zero (apply str (repeat 1000000 \0)))
    #'user/long-zero
    user=> (time (dotimes [_ 100] (clojure.edn/read-string long-zero)))
    "Elapsed time: 3978.541417 msecs"
    nil
    user=> (time (dotimes [_ 100] (clojure.edn/read-string "0")))
    "Elapsed time: 0.268291 msecs"
    nil

Secondly, in such situation, it is harder to identify who cause service degradation. Typically, developers will probably print incoming data *after* it is red by something like clojure.edn. And after printing it will be not a large integer (which is fairly easy to spot in logs) but a humble small number.

So, from my perspective, it will be better at least from the point of discoverability of a root cause to forbid leading zeroes in edn' integers representation completely.
ago by
How can it be caught at the border via validation without actually parsing the number? And if the parsing is done during validation, then an all-zero number can also be validated just fine, without any changes to `clojure.edn`.

But it feels like bike shedding at this point, to be honest. Even if you disallow all such integers, you still don't really improve the situation w.r.t. DoS because leading zeros are explicitly allowed in the `exp` part of floating point numbers.
ago by
Didn't I mention exponent part in the original post? :)

> How can it be caught at the border via validation without actually parsing the number?

You can detect leading zeros during parsing -> throw exception without finishing the number because leading zeroes are forbidden. Saying this with a confidence because I recently finished such thing.

> But it feels like bike shedding at this point, to be honest.

I would like to know someone else opinion on this topic :) My thoughts are all there.
ago by
> Didn't I mention exponent part in the original post? :)

I'm not saying that you missed something. My point is that regardless of what you do to integer parsing, floating point parsing already enables DoS, and it cannot be changed unless the format spec is made stricter, which is a breaking change.
ago by
what else can I say -

Please log in or register to answer this question.

...