516 lines
32 KiB
HTML
516 lines
32 KiB
HTML
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<link rel="stylesheet" href="../../coverage.css"/> <title> dog_and_duck/quack/quack.clj </title>
|
|
</head>
|
|
<body>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
001 (ns dog-and-duck.quack.quack
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
002 "Validator for ActivityPub objects: if it walks like a duck, and it quacks
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
003 like a duck...
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
004
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
005 **NOTE THAT the ActivityPub spec
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
006 [says](https://www.w3.org/TR/activitypub/#obj)
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
007
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
008 > Servers SHOULD validate the content they receive to avoid content
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
009 > spoofing attacks
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
010
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
011 but in practice ActivityPub content collected in the wild bears only
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
012 a hazy relationship to the spec, so this is difficult. I suspect that
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
013 I may have to implement a `*strict*` dynamic variable, so that users can
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
014 toggle some checks off."
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
015
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
016 (:require [dog-and-duck.quack.picky :refer [*reject-severity* activity-faults
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
017 actor-faults filter-severity link-faults
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
018 object-faults persistent-object-faults]])
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
019
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
020 (:import [java.net URI URISyntaxException]))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
021
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
022 ;;; Copyright (C) Simon Brooke, 2022
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
023
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
024 ;;; This program is free software; you can redistribute it and/or
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
025 ;;; modify it under the terms of the GNU General Public License
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
026 ;;; as published by the Free Software Foundation; either version 2
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
027 ;;; of the License, or (at your option) any later version.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
028
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
029 ;;; This program is distributed in the hope that it will be useful,
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
030 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
031 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
032 ;;; GNU General Public License for more details.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
033
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
034 ;;; You should have received a copy of the GNU General Public License
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
035 ;;; along with this program; if not, write to the Free Software
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
036 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
037
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
038 (defn object?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
039 "Returns `true` iff `x` is recognisably an ActivityStreams object.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
040
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
041 **NOTE THAT** The ActivityStreams spec
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
042 [says](https://www.w3.org/TR/activitystreams-core/#object):
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
043
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
044 > All properties are optional (including the id and type)
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
045
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
046 But we are *just not having that*, because otherwise we're flying blind.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
047 We *shall* reject objects lacking at least `:type`. Missing `:id` keys are
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
048 tolerable because they represent transient objects, which we expect to
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
049 handle.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
050
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
051 **NOTE THAT** The ActivityPub spec [says](https://www.w3.org/TR/activitypub/#obj)
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
052
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
053 > Implementers SHOULD include the ActivityPub context in their object
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
054 > definitions
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
055
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
056 but in samples found in the wild they typically don't."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
057 ([x]
|
|
</span><br/>
|
|
<span class="covered" title="4 out of 4 forms covered">
|
|
058 (object? x *reject-severity*))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
059 ([x severity]
|
|
</span><br/>
|
|
<span class="covered" title="8 out of 8 forms covered">
|
|
060 (empty? (filter-severity (object-faults x) severity))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
061
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
062 (defn persistent-object?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
063 "`true` iff `x` is a persistent object.
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
064
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
065 Transient objects in ActivityPub are not required to have an `id` key, but persistent
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
066 ones must have a key, and it must be an IRI (but normally a URI)."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
067 ([x]
|
|
</span><br/>
|
|
<span class="covered" title="4 out of 4 forms covered">
|
|
068 (persistent-object? x *reject-severity*))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
069 ([x severity]
|
|
</span><br/>
|
|
<span class="covered" title="8 out of 8 forms covered">
|
|
070 (empty? (filter-severity (persistent-object-faults x) severity))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
071
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
072 (defn actor?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
073 "Returns `true` if `x` quacks like an actor, else false."
|
|
</span><br/>
|
|
<span class="covered" title="4 out of 4 forms covered">
|
|
074 ([x] (actor? x *reject-severity*))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
075 ([x severity]
|
|
</span><br/>
|
|
<span class="covered" title="8 out of 8 forms covered">
|
|
076 (empty? (filter-severity (actor-faults x) severity))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
077
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
078 (defn actor-or-uri?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
079 "`true` if `x` is either a URI or an actor.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
080
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
081 **TODO**: I need to decide about whether to reify referenced objects
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
082 before validation or after. After reification, every reference to an actor
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
083 *must be* to an actor object, but before, may only be to a URI pointing to
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
084 one."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
085 [x]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
086 (try
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 5 forms covered">
|
|
087 (and
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 9 forms covered">
|
|
088 (cond (string? x) (uri? (URI. x))
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
089 :else (actor? x))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
090 true)
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
091 (catch URISyntaxException _ false)
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
092 (catch NullPointerException _ false)))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
093
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
094 (defn activity?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
095 "`true` iff `x` quacks like an activity, else false."
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
096 ([x] (activity? x *reject-severity*))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
097 ([x severity]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 8 forms covered">
|
|
098 (empty? (filter-severity (activity-faults x) severity))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
099
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
100 (defn link?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
101 "`true` iff `x` quacks like a link, else false."
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
102 ([x] (link? x *reject-severity*))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
103 ([x severity]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 8 forms covered">
|
|
104 (empty? (filter-severity (link-faults x) severity))))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
105
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
106 (defn link-or-uri?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
107 "`true` iff `x` is either a URI or a link, else false.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
108
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
109 There are several points in the specification where e.g. the `:image`
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
110 property (if present) may be either a link or a URI."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
111 [x]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 5 forms covered">
|
|
112 (and
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 9 forms covered">
|
|
113 (cond (string? x) (uri? (URI. x))
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 3 forms covered">
|
|
114 :else (link? x))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
115 true))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
116
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
117 (defn collection?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
118 "`true` iff `x` quacks like a collection of type `object-type`, else `false`.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
119
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
120 With one argument, will recognise plain collections and ordered collections,
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
121 but (currently) not collection pages."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
122 ([x ^String object-type]
|
|
</span><br/>
|
|
<span class="partial" title="10 out of 11 forms covered">
|
|
123 (let [items (or (:items x) (:orderedItems x))]
|
|
</span><br/>
|
|
<span class="partial" title="14 out of 17 forms covered">
|
|
124 (and
|
|
</span><br/>
|
|
<span class="partial" title="5 out of 6 forms covered">
|
|
125 (cond
|
|
</span><br/>
|
|
<span class="partial" title="3 out of 8 forms covered">
|
|
126 (:items x) (nil? (:orderedItems x))
|
|
</span><br/>
|
|
<span class="covered" title="8 out of 8 forms covered">
|
|
127 (:orderedItems x) (nil? (:items x)) ;; can't have both properties
|
|
</span><br/>
|
|
<span class="covered" title="5 out of 5 forms covered">
|
|
128 (integer? (:totalItems x)) true ;; can have neither, provided it has totalItems.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
129 :else false)
|
|
</span><br/>
|
|
<span class="covered" title="3 out of 3 forms covered">
|
|
130 (object? x)
|
|
</span><br/>
|
|
<span class="covered" title="5 out of 5 forms covered">
|
|
131 (= (:type x) object-type)
|
|
</span><br/>
|
|
<span class="partial" title="2 out of 3 forms covered">
|
|
132 (if items
|
|
</span><br/>
|
|
<span class="partial" title="10 out of 12 forms covered">
|
|
133 (and (coll? items)
|
|
</span><br/>
|
|
<span class="covered" title="4 out of 4 forms covered">
|
|
134 (every? object? items) ;; if there are items, they must form a
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
135 ;; collection of objects.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
136 true)
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
137 true) ;; but it's OK if there aren't.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
138 true)
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
139 ;; test for totalItems not done here, because collection pages don't
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
140 ;; have it.
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
141 ))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
142 ([x]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 9 forms covered">
|
|
143 (and
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 10 forms covered">
|
|
144 (or (collection? x "Collection")
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 2 forms covered">
|
|
145 (collection? x "OrderedCollection"))
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 5 forms covered">
|
|
146 (integer? (:totalItems x))
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
147 true)))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
148
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
149 (defn unordered-collection?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
150 "`true` iff `x` quacks like an unordered collection, else `false`."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
151 [x]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 18 forms covered">
|
|
152 (and (collection? x "Collection") (integer? (:totalItems x)) true))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
153
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
154 (defn ordered-collection?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
155 "`true` iff `x` quacks like an ordered collection, else `false`."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
156 [x]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 18 forms covered">
|
|
157 (and (collection? x "OrderedCollection") (integer? (:totalItems x)) true))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
158
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
159 (defn collection-page?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
160 "`true` iff `x` quacks like a page in a paged collection, else `false`."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
161 [x]
|
|
</span><br/>
|
|
<span class="not-covered" title="0 out of 4 forms covered">
|
|
162 (collection? x "CollectionPage"))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
163
|
|
</span><br/>
|
|
<span class="covered" title="1 out of 1 forms covered">
|
|
164 (defn ordered-collection-page?
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
165 "`true` iff `x` quacks like a page in an ordered paged collection, else `false`."
|
|
</span><br/>
|
|
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
166 [x]
|
|
</span><br/>
|
|
<span class="covered" title="4 out of 4 forms covered">
|
|
167 (collection? x "OrderedCollectionPage"))
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
168
|
|
</span><br/>
|
|
<span class="blank" title="0 out of 0 forms covered">
|
|
169
|
|
</span><br/>
|
|
</body>
|
|
</html>
|