clojure-spec

zendevil.eth 2022-02-28T19:24:19.419069Z

So here we have :req and :opt to be used in s/keys. That doesn’t stop other keys tht are not listed in opt or req from being accepted right? How do you limit the keys that can be accepted to only the req and opt keys?

2022-02-28T19:24:58.254469Z

that's deliberately not supported by spec. use select-keys or similar.

zendevil.eth 2022-02-28T19:30:25.546659Z

why this design decision?

2022-02-28T19:39:09.433439Z

the focus was on enforcing minimum needs.

2022-02-28T19:40:03.172879Z

spec 2 does allow for this, because there are legitimate use cases for it. and the implicit checking of bonus keys with their own specs can be a DoS attack vector, if those other keys have slow specs.

2022-02-28T19:40:51.218229Z

worse, since it's extra data getting validated, you can send perfectly valid requests that take tens of seconds or more to validate, but don't stand out in monitoring or logs as errors or unusually large...

zendevil.eth 2022-02-28T19:41:42.039739Z

what’s the solution to the dos attack when comprehensively validating the input

zendevil.eth 2022-02-28T19:42:49.700029Z

so we should use spec2 right?

2022-02-28T19:43:02.843759Z

well, perhaps not. it's still alpha. just mentioning it.

zendevil.eth 2022-02-28T19:43:15.736249Z

isn’t spec alpha too?

2022-02-28T19:43:31.401109Z

I'd say the most straightforward thing is to just select-keys first, then validate with spec 1.

2022-02-28T19:43:54.760119Z

sure, but it's been battle tested in a way the spec 2 hasn't.

Alex Miller (Clojure team) 2022-02-28T19:46:55.618959Z

spec 2 has not been released, and you shouldn't use it for real stuff

2022-02-28T19:55:05.158299Z

Stating the obvious, but you can always use a custom predicate to limit the spec in addition to s/keys

👍 1
zendevil.eth 2022-02-28T22:09:32.157209Z

Let’s say I’m trying to conform a map to a spec. I want each key of the map to have values of a certain type. How can I write a spec for that?

zendevil.eth 2022-02-28T22:10:18.495409Z

I have this:

(s/def ::create-sessions-params (s/and (s/keys :opt-un [::students ::start_date ::start_time ::timezone ::instructor])))

zendevil.eth 2022-02-28T22:10:56.394549Z

but then for each key, I want to impose a type to the key’s value too

zendevil.eth 2022-02-28T22:10:59.986249Z

how will I do that?

ghadi 2022-02-28T22:12:07.416679Z

(s/def ::instructor string?) etc.

ghadi 2022-02-28T22:12:30.008939Z

(the s/and in your example doesn't do anything - remove it)

ghadi 2022-02-28T22:13:08.476509Z

I can read your question in several ways, might want to elaborate

zendevil.eth 2022-02-28T22:21:24.901859Z

@ghadi what would be the best way to restrict the keys to just the keys in the opt?

ghadi 2022-02-28T22:21:40.114499Z

?

ghadi 2022-02-28T22:21:45.147529Z

restrict what?

zendevil.eth 2022-02-28T22:22:07.907259Z

like it shouldn’t conform if there are keys other than the ones in the :opt-un list

zendevil.eth 2022-02-28T22:22:28.306479Z

opt-un or req-un or opt or req

ghadi 2022-02-28T22:23:47.437789Z

same answer as in the thread above - transform lightly, then check

ghadi 2022-02-28T22:24:14.693989Z

select-keys or similar

zendevil.eth 2022-02-28T22:31:04.803349Z

can I use one of these libraries to achieve the effect rather than select keys: https://github.com/metosin/spec-tools https://github.com/wilkerlucio/spec-coerce

zendevil.eth 2022-02-28T23:21:27.870119Z

So would this be good?:

(defn keys-conform? [m k]
 (every? (set k) (keys m)))
(def create-sessions-keys [::students ::start_date ::start_time ::timezone ::instructor])
(s/def ::create-sessions-params (s/and (s/keys :opt-un create-sessions-keys) #(keys-conform? % create-sessions-keys)))

zendevil.eth 2022-02-28T23:21:36.213109Z

keys-conform makes sure that the keys in the map are only the ones that are in the keys vector

seancorfield 2022-02-28T23:28:45.431579Z

s/keys is a macro and cannot be passed a var like that, as I recall.

Alex Miller (Clojure team) 2022-02-28T23:28:47.140379Z

unfortunately, that won't work in s/keys

zendevil.eth 2022-02-28T23:44:59.429039Z

that should definitely be a feature in clojure spec

zendevil.eth 2022-02-28T23:45:13.930179Z

how come we can’t use variables in it

Alex Miller (Clojure team) 2022-02-28T23:47:52.649099Z

spec forms are macros and at compile time, we wouldn't know your runtime key set. other choices have been made for spec 2. It is possible to retrieve the keys from the spec using specs on the spec forms themselves but it's pretty ugly to do so - most people end up just walking the form as data to do this

Alex Miller (Clojure team) 2022-02-28T23:28:56.369079Z

so you'd have to repeat the vector of keys there

Alex Miller (Clojure team) 2022-02-28T23:29:22.670799Z

btw, there is a faq entry about this here: https://clojure.org/guides/faq#exclusive_keys

seancorfield 2022-02-28T23:30:11.782539Z

What we do is get the spec form (via the Spec API) and lift the keys out of it to use in other contexts -- we treat the Spec itself as the "system of record".

zendevil.eth 2022-02-28T23:34:39.926319Z

@seancorfield can you please elaborate?

2022-02-28T23:51:06.796929Z

user=> (s/def ::f (s/keys :req [:a/x]))
:user/f
user=> (s/form ::f)
(clojure.spec.alpha/keys :req [:a/x])
user=>

2022-02-28T23:51:17.020579Z

you use s/form to inspect specs