Fork me on GitHub
#clojure-spec
<
2022-02-28
>
zendevil.eth19:02:19

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?

Braden Shepherdson19:02:58

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

zendevil.eth19:02:25

why this design decision?

Braden Shepherdson19:02:09

the focus was on enforcing minimum needs.

Braden Shepherdson19:02:03

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.

Braden Shepherdson19:02:51

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.eth19:02:42

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

zendevil.eth19:02:49

so we should use spec2 right?

Braden Shepherdson19:02:02

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

zendevil.eth19:02:15

isn’t spec alpha too?

Braden Shepherdson19:02:31

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

Braden Shepherdson19:02:54

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

Alex Miller (Clojure team)19:02:55

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

jjttjj19:02:05

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

👍 1
zendevil.eth22:02:32

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.eth22:02:18

I have this:

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

zendevil.eth22:02:56

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

zendevil.eth22:02:59

how will I do that?

ghadi22:02:07

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

ghadi22:02:30

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

ghadi22:02:08

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

zendevil.eth22:02:24

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

ghadi22:02:45

restrict what?

zendevil.eth22:02:07

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

zendevil.eth22:02:28

opt-un or req-un or opt or req

ghadi22:02:47

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

ghadi22:02:14

select-keys or similar

zendevil.eth22:02:04

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.eth23:02:27

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.eth23:02:36

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

seancorfield23:02:45

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

Alex Miller (Clojure team)23:02:47

unfortunately, that won't work in s/keys

zendevil.eth23:02:59

that should definitely be a feature in clojure spec

zendevil.eth23:02:13

how come we can’t use variables in it

Alex Miller (Clojure team)23:02:52

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)23:02:56

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

seancorfield23:02:11

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.eth23:02:39

@seancorfield can you please elaborate?

hiredman23:02:06

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

hiredman23:02:17

you use s/form to inspect specs