eyeling 1.12.14 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HANDBOOK.md +54 -1
- package/README.md +15 -1
- package/examples/annotation.n3 +2 -2
- package/examples/context-association.n3 +4 -4
- package/examples/odrl-dpv-risk-ranked.n3 +479 -0
- package/examples/odrl-risk-mitigation.n3 +870 -0
- package/examples/output/odrl-dpv-risk-ranked.n3 +95 -0
- package/examples/output/odrl-risk-mitigation.n3 +207 -0
- package/examples/output/socrates.n3 +1 -0
- package/examples/reifies.n3 +2 -1
- package/examples/socrates.n3 +3 -0
- package/eyeling-builtins.ttl +3 -0
- package/eyeling.js +243 -20
- package/lib/cli.js +34 -10
- package/lib/engine.js +194 -9
- package/lib/entry.js +2 -0
- package/lib/parser.js +8 -1
- package/lib/prelude.js +5 -0
- package/package.json +1 -1
- package/tools/n3gen.js +85 -27
package/HANDBOOK.md
CHANGED
|
@@ -761,6 +761,8 @@ A predicate is treated as builtin if:
|
|
|
761
761
|
|
|
762
762
|
Super restricted mode exists to let you treat all other predicates as ordinary facts/rules without any built-in evaluation.
|
|
763
763
|
|
|
764
|
+
**Note on `log:query`:** Eyeling also recognizes a special _top-level_ directive of the form `{...} log:query {...}.` to **select which results to print**. This is **not** a builtin predicate (it is not evaluated as part of goal solving); it is handled by the parser/CLI/output layer. See §11.3.5 below and Chapter 13 for details.
|
|
765
|
+
|
|
764
766
|
### 11.2 Built-ins return multiple solutions
|
|
765
767
|
|
|
766
768
|
Every builtin returns a list of substitution _deltas_.
|
|
@@ -1353,6 +1355,47 @@ As _builtins_, `log:implies` and `log:impliedBy` let you **inspect the currently
|
|
|
1353
1355
|
|
|
1354
1356
|
Each enumerated rule is standardized apart (fresh variable names) before unification so you can safely query over it.
|
|
1355
1357
|
|
|
1358
|
+
### Top-level directive: `log:query` (output selection)
|
|
1359
|
+
|
|
1360
|
+
**Shape (top level only):**
|
|
1361
|
+
|
|
1362
|
+
```n3
|
|
1363
|
+
{ ...premise... } log:query { ...conclusion... }.
|
|
1364
|
+
```
|
|
1365
|
+
|
|
1366
|
+
`log:query` is best understood as an **output projection**, not as a rule and not as a normal builtin:
|
|
1367
|
+
|
|
1368
|
+
- Eyeling still computes the saturated forward closure (facts + rules, including backward-rule proofs where needed).
|
|
1369
|
+
- It then proves the **premise formula** as a goal (as if it were fed to `log:includes` in the global scope).
|
|
1370
|
+
- For every solution, it instantiates the **conclusion formula** and collects the resulting triples.
|
|
1371
|
+
- The final output is the **set of unique ground triples** from those instantiated conclusions.
|
|
1372
|
+
|
|
1373
|
+
This is “forward-rule-like” in spirit (premise ⇒ conclusion), but the instantiated conclusion triples are **not added back into the fact store**; they are just what Eyeling prints.
|
|
1374
|
+
|
|
1375
|
+
**Important details:**
|
|
1376
|
+
|
|
1377
|
+
- Only **top-level** `{...} log:query {...}.` directives are recognized. Inside quoted formulas (or inside rule bodies/heads) it is just an ordinary triple.
|
|
1378
|
+
- Query-mode output depends on the saturated closure, so it cannot be streamed; `--stream` has no effect when any `log:query` directives are present.
|
|
1379
|
+
- If you want _logical_ querying inside a rule/proof, use `log:includes` (and optionally `log:conclusion`) instead.
|
|
1380
|
+
|
|
1381
|
+
**Example (project a result set):**
|
|
1382
|
+
|
|
1383
|
+
```n3
|
|
1384
|
+
@prefix : <urn:ex:>.
|
|
1385
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
1386
|
+
|
|
1387
|
+
{ :a :p ?x } => { :a :q ?x }.
|
|
1388
|
+
:a :p :b.
|
|
1389
|
+
|
|
1390
|
+
{ :a :q ?x } log:query { :result :x ?x }.
|
|
1391
|
+
```
|
|
1392
|
+
|
|
1393
|
+
Output (only):
|
|
1394
|
+
|
|
1395
|
+
```n3
|
|
1396
|
+
:result :x :b .
|
|
1397
|
+
```
|
|
1398
|
+
|
|
1356
1399
|
### Scoped proof inside formulas: `log:includes` and friends
|
|
1357
1400
|
|
|
1358
1401
|
#### `log:includes`
|
|
@@ -1809,6 +1852,14 @@ See also: [Chapter 14 — Entry points: CLI, bundle exports, and npm API](#ch14)
|
|
|
1809
1852
|
|
|
1810
1853
|
By default, Eyeling prints **newly derived forward facts** (the heads of fired `=>` rules), serialized as N3. It does **not** reprint your input facts.
|
|
1811
1854
|
|
|
1855
|
+
If the input contains one or more **top-level** `log:query` directives:
|
|
1856
|
+
|
|
1857
|
+
```n3
|
|
1858
|
+
{ ...premise... } log:query { ...conclusion... }.
|
|
1859
|
+
```
|
|
1860
|
+
|
|
1861
|
+
Eyeling still computes the saturated forward closure, but it prints only the **unique instantiated conclusion triples** of those `log:query` directives (instead of all newly derived facts). This is useful when you want a forward-rule-like projection of results.
|
|
1862
|
+
|
|
1812
1863
|
For proof/explanation output and output modes, see:
|
|
1813
1864
|
|
|
1814
1865
|
- [Chapter 13 — Printing, proofs, and the user-facing output](#ch13)
|
|
@@ -1835,6 +1886,8 @@ Options:
|
|
|
1835
1886
|
-v, --version Print version and exit.
|
|
1836
1887
|
```
|
|
1837
1888
|
|
|
1889
|
+
Note: when `log:query` directives are present, Eyeling cannot stream output (the selected results depend on the saturated closure), so `--stream` has no effect in that mode.
|
|
1890
|
+
|
|
1838
1891
|
See also:
|
|
1839
1892
|
|
|
1840
1893
|
- [Chapter 13 — Printing, proofs, and the user-facing output](#ch13)
|
|
@@ -2031,7 +2084,7 @@ If you don’t want “stop the world”, derive a `:Violation` fact instead, an
|
|
|
2031
2084
|
The most robust way to keep LLM-generated logic plausible is to make it live under tests:
|
|
2032
2085
|
|
|
2033
2086
|
- Keep tiny **fixtures** (facts) alongside the rules.
|
|
2034
|
-
- Run Eyeling to produce the **derived closure** (Eyeling
|
|
2087
|
+
- Run Eyeling to produce the **derived closure** (Eyeling emits only newly derived forward facts by default, can optionally include compact proof comments, and can also use `log:query` directives to project a specific result set).
|
|
2035
2088
|
- Compare against an expected output (“golden file”) in CI.
|
|
2036
2089
|
|
|
2037
2090
|
This turns rule edits into a normal change-management loop: diffs are explicit, reviewable, and reproducible.
|
package/README.md
CHANGED
|
@@ -4,7 +4,8 @@ A compact [Notation3 (N3)](https://notation3.org/) reasoner in **JavaScript**.
|
|
|
4
4
|
|
|
5
5
|
- Single self-contained bundle (`eyeling.js`), no external runtime deps
|
|
6
6
|
- Forward (`=>`) + backward (`<=`) chaining over Horn-style rules
|
|
7
|
-
- Outputs only **newly derived** forward facts (optionally with compact proof comments)
|
|
7
|
+
- Outputs only **newly derived** forward facts by default (optionally with compact proof comments)
|
|
8
|
+
- If the input contains one or more top-level `{ ... } log:query { ... }.` directives, the output becomes the **unique instantiated conclusion triples** of those queries (a forward-rule-like projection)
|
|
8
9
|
- Works in Node.js and fully client-side (browser/worker)
|
|
9
10
|
|
|
10
11
|
## Links
|
|
@@ -45,6 +46,16 @@ See all options:
|
|
|
45
46
|
npx eyeling --help
|
|
46
47
|
```
|
|
47
48
|
|
|
49
|
+
### log:query output selection
|
|
50
|
+
|
|
51
|
+
If your input contains one or more **top-level** directives of the form:
|
|
52
|
+
|
|
53
|
+
```n3
|
|
54
|
+
{ ?x a :Human. } log:query { ?x a :Mortal. }.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Eyeling will still compute the saturated forward closure, but it will **print only** the **unique instantiated conclusion triples** of those `log:query` directives (instead of printing all newly derived forward facts).
|
|
58
|
+
|
|
48
59
|
### JavaScript API
|
|
49
60
|
|
|
50
61
|
CommonJS:
|
|
@@ -79,6 +90,9 @@ const { closureN3 } = eyeling.reasonStream(input, {
|
|
|
79
90
|
proof: false,
|
|
80
91
|
onDerived: ({ triple }) => console.log(triple),
|
|
81
92
|
});
|
|
93
|
+
|
|
94
|
+
// With log:query directives present, closureN3 contains the query-selected triples.
|
|
95
|
+
// The return value also includes `queryMode`, `queryTriples`, and `queryDerived`.
|
|
82
96
|
```
|
|
83
97
|
|
|
84
98
|
> Note: the npm `reason()` helper shells out to the bundled `eyeling.js` CLI for simplicity and robustness.
|
package/examples/annotation.n3
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
@prefix : <http://example.com/> .
|
|
2
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
2
3
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
3
|
-
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
4
4
|
|
|
5
5
|
:a :name "Alice" .
|
|
6
|
-
:t
|
|
6
|
+
:t log:nameOf { :a :name "Alice" . } .
|
|
7
7
|
:t :statedBy :bob .
|
|
8
8
|
:t :recorded "2021-07-07"^^xsd:date .
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
@prefix rdfg: <http://www.w3.org/2009/rdfg#> .
|
|
2
1
|
@prefix : <http://example.org/#> .
|
|
3
2
|
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
4
3
|
@prefix sec: <https://w3id.org/security#> .
|
|
5
4
|
@prefix skolem: <https://eyereasoner.github.io/.well-known/genid/5649fff4-464d-5969-88ed-956a6b5f0d90#> .
|
|
5
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
6
6
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
7
7
|
|
|
8
|
-
skolem:g0
|
|
8
|
+
skolem:g0 log:nameOf {
|
|
9
9
|
:Bob foaf:name "Bob" .
|
|
10
10
|
} .
|
|
11
11
|
|
|
12
|
-
skolem:g1
|
|
12
|
+
skolem:g1 log:nameOf {
|
|
13
13
|
skolem:g0 sec:proof _:dataSignature .
|
|
14
14
|
_:signature1 a sec:DataIntegrityProof .
|
|
15
15
|
_:signature1 sec:cryptosuite "ecdsa-rdfc-2019" .
|
|
@@ -22,7 +22,7 @@ skolem:g1 rdfg:isGraph {
|
|
|
22
22
|
_:signature1 sec:validUntil "2025-04-03T00:00:00.000Z"^^xsd:dateTime .
|
|
23
23
|
} .
|
|
24
24
|
|
|
25
|
-
_:g3
|
|
25
|
+
_:g3 log:nameOf {
|
|
26
26
|
skolem:g1 sec:proof _:signature2 .
|
|
27
27
|
_:signature2 a sec:DataIntegrityProof .
|
|
28
28
|
_:signature2 sec:cryptosuite "ecdsa-rdfc-2019" .
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
# ===========================================================================================
|
|
2
|
+
# ODRL + DPV risk assessment with ranked, explainable output.
|
|
3
|
+
#
|
|
4
|
+
# What this file does
|
|
5
|
+
# - Models an agreement as an ODRL policy (odrl:Policy) containing permissions,
|
|
6
|
+
# prohibitions, duties, and constraints. ODRL is expressive enough to encode the
|
|
7
|
+
# normative “may/must/must-not” structure of TOS clauses as RDF.
|
|
8
|
+
# - Links each ODRL rule to a clause resource (:Clause) to keep human-readable text
|
|
9
|
+
# while preserving machine-readable structure for reasoning.
|
|
10
|
+
# - Uses N3 rules with log:includes / log:notIncludes to detect missing safeguards
|
|
11
|
+
# (e.g., missing notice constraints, missing inform duties, missing consent constraints).
|
|
12
|
+
# - Generates DPV risks (dpv:Risk) and classifies them using DPV-RISK concepts:
|
|
13
|
+
# risk:hasRiskSource, dpv:hasConsequence, dpv:hasImpact, dpv:hasSeverity, dpv:hasRiskLevel.
|
|
14
|
+
# - Produces mitigations as dpv:RiskMitigationMeasure resources and attaches them
|
|
15
|
+
# to risks with dpv:isMitigatedByMeasure.
|
|
16
|
+
# - Computes a numeric score for each risk and prints a ranked, explainable report.
|
|
17
|
+
#
|
|
18
|
+
# Ranking / output
|
|
19
|
+
# - Output strings are emitted as log:outputString triples.
|
|
20
|
+
# - When running with Eyeling “strings” mode (-r), strings are printed in deterministic
|
|
21
|
+
# order based on their subject key. The program encodes ranking via an “inverse score”
|
|
22
|
+
# key (e.g., 1000 - score) so higher-risk items appear first.
|
|
23
|
+
#
|
|
24
|
+
# References
|
|
25
|
+
# - N3 spec: https://w3c.github.io/N3/spec/
|
|
26
|
+
# - Eyeling builtins: https://eyereasoner.github.io/eyeling/HANDBOOK#ch11
|
|
27
|
+
# - ODRL vocab: https://www.w3.org/TR/odrl-vocab/
|
|
28
|
+
# - DPV risk module: https://w3c.github.io/dpv/2.0/dpv/modules/risk.html
|
|
29
|
+
# - DPV-RISK: https://w3id.org/dpv/risk
|
|
30
|
+
# ===========================================================================================
|
|
31
|
+
|
|
32
|
+
@prefix : <https://example.org/odrl-dpv-risk-ranked#> .
|
|
33
|
+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
|
|
34
|
+
@prefix dpv: <https://w3id.org/dpv#> .
|
|
35
|
+
@prefix risk: <https://w3id.org/dpv/risk#> .
|
|
36
|
+
@prefix dct: <http://purl.org/dc/terms/> .
|
|
37
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
38
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
39
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
|
|
40
|
+
@prefix string:<http://www.w3.org/2000/10/swap/string#> .
|
|
41
|
+
@prefix tosl: <https://example.org/tosl-profile#> .
|
|
42
|
+
|
|
43
|
+
# ---------------------------
|
|
44
|
+
# 1) Consumer profile (needs)
|
|
45
|
+
# ---------------------------
|
|
46
|
+
|
|
47
|
+
:ConsumerExample a :ConsumerProfile ;
|
|
48
|
+
dct:title "Example consumer profile" ;
|
|
49
|
+
:hasNeed :Need_DataCannotBeRemoved,
|
|
50
|
+
:Need_ChangeOnlyWithPriorNotice,
|
|
51
|
+
:Need_NoSharingWithoutConsent,
|
|
52
|
+
:Need_DataPortability .
|
|
53
|
+
|
|
54
|
+
:Need_DataCannotBeRemoved a :Need ;
|
|
55
|
+
:importance "20"^^xsd:integer ;
|
|
56
|
+
dct:description "Provider must not remove the consumer account/data." .
|
|
57
|
+
|
|
58
|
+
:Need_ChangeOnlyWithPriorNotice a :Need ;
|
|
59
|
+
:importance "15"^^xsd:integer ;
|
|
60
|
+
:minNoticeDays "14"^^xsd:integer ;
|
|
61
|
+
dct:description "Agreement may change only with prior notice (>= 14 days)." .
|
|
62
|
+
|
|
63
|
+
:Need_NoSharingWithoutConsent a :Need ;
|
|
64
|
+
:importance "12"^^xsd:integer ;
|
|
65
|
+
dct:description "No data sharing without explicit consent." .
|
|
66
|
+
|
|
67
|
+
:Need_DataPortability a :Need ;
|
|
68
|
+
:importance "10"^^xsd:integer ;
|
|
69
|
+
dct:description "Consumer must be able to export their data." .
|
|
70
|
+
|
|
71
|
+
# ------------------------------------------
|
|
72
|
+
# 2) Agreement (ODRL policy graph + clauses)
|
|
73
|
+
# ------------------------------------------
|
|
74
|
+
|
|
75
|
+
:Agreement1 a :Agreement ;
|
|
76
|
+
dct:title "Example Agreement" ;
|
|
77
|
+
:policyGraph {
|
|
78
|
+
:Policy1 a odrl:Policy ;
|
|
79
|
+
odrl:permission :PermDeleteAccount,
|
|
80
|
+
:PermChangeTerms,
|
|
81
|
+
:PermShareData ;
|
|
82
|
+
odrl:prohibition :ProhibitExportData .
|
|
83
|
+
|
|
84
|
+
# Clause C1: remove account/data without safeguards
|
|
85
|
+
:PermDeleteAccount a odrl:Permission ;
|
|
86
|
+
odrl:assigner :Provider ;
|
|
87
|
+
odrl:assignee odrl:consumer ;
|
|
88
|
+
odrl:action tosl:removeAccount ;
|
|
89
|
+
odrl:target :UserAccount ;
|
|
90
|
+
:clause :ClauseC1 .
|
|
91
|
+
|
|
92
|
+
# Clause C2: change terms with noticeDays >= 3 (may be too short for the consumer)
|
|
93
|
+
:PermChangeTerms a odrl:Permission ;
|
|
94
|
+
odrl:assigner :Provider ;
|
|
95
|
+
odrl:assignee odrl:consumer ;
|
|
96
|
+
odrl:action tosl:changeTerms ;
|
|
97
|
+
odrl:target :AgreementText ;
|
|
98
|
+
odrl:duty [
|
|
99
|
+
a odrl:Duty ;
|
|
100
|
+
odrl:action odrl:inform ;
|
|
101
|
+
odrl:constraint [
|
|
102
|
+
a odrl:Constraint ;
|
|
103
|
+
odrl:leftOperand tosl:noticeDays ;
|
|
104
|
+
odrl:operator odrl:gteq ;
|
|
105
|
+
odrl:rightOperand "3"^^xsd:integer
|
|
106
|
+
]
|
|
107
|
+
] ;
|
|
108
|
+
:clause :ClauseC2 .
|
|
109
|
+
|
|
110
|
+
# Clause C3: share data without explicit consent safeguard
|
|
111
|
+
:PermShareData a odrl:Permission ;
|
|
112
|
+
odrl:assigner :Provider ;
|
|
113
|
+
odrl:assignee odrl:consumer ;
|
|
114
|
+
odrl:action tosl:shareData ;
|
|
115
|
+
odrl:target :UserData ;
|
|
116
|
+
:clause :ClauseC3 .
|
|
117
|
+
|
|
118
|
+
# Clause C4: prohibit export (hurts portability)
|
|
119
|
+
:ProhibitExportData a odrl:Prohibition ;
|
|
120
|
+
odrl:assigner :Provider ;
|
|
121
|
+
odrl:assignee odrl:consumer ;
|
|
122
|
+
odrl:action tosl:exportData ;
|
|
123
|
+
odrl:target :UserData ;
|
|
124
|
+
:clause :ClauseC4 .
|
|
125
|
+
} .
|
|
126
|
+
|
|
127
|
+
:ClauseC1 a :Clause ; :clauseId "C1" ; :text "Provider may remove the user account (and associated data) at its discretion." .
|
|
128
|
+
:ClauseC2 a :Clause ; :clauseId "C2" ; :text "Provider may change terms by informing users at least 3 days in advance." .
|
|
129
|
+
:ClauseC3 a :Clause ; :clauseId "C3" ; :text "Provider may share user data with partners for business purposes." .
|
|
130
|
+
:ClauseC4 a :Clause ; :clauseId "C4" ; :text "Users are not permitted to export their data." .
|
|
131
|
+
|
|
132
|
+
:ProcessContext1 a dpv:Process ;
|
|
133
|
+
dct:source :Agreement1 ;
|
|
134
|
+
dct:title "Service operation under Agreement1" .
|
|
135
|
+
|
|
136
|
+
# ------------------------------------------------------------------------------------
|
|
137
|
+
# 3) Risk rules (ODRL -> DPV/DPV-RISK) + mitigations
|
|
138
|
+
# IMPORTANT: only match ODRL structure inside log:includes; clause text is outside.
|
|
139
|
+
# ------------------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
# R1: remove account/data WITHOUT notice constraint AND WITHOUT inform duty
|
|
142
|
+
{
|
|
143
|
+
:Agreement1 :policyGraph ?G .
|
|
144
|
+
:ConsumerExample :hasNeed :Need_DataCannotBeRemoved .
|
|
145
|
+
:Need_DataCannotBeRemoved :importance ?w .
|
|
146
|
+
|
|
147
|
+
?G log:includes {
|
|
148
|
+
:PermDeleteAccount a odrl:Permission ;
|
|
149
|
+
odrl:action tosl:removeAccount ;
|
|
150
|
+
:clause ?clause .
|
|
151
|
+
}.
|
|
152
|
+
|
|
153
|
+
?G log:notIncludes {
|
|
154
|
+
:PermDeleteAccount odrl:constraint [
|
|
155
|
+
odrl:leftOperand tosl:noticeDays
|
|
156
|
+
] .
|
|
157
|
+
}.
|
|
158
|
+
|
|
159
|
+
?G log:notIncludes {
|
|
160
|
+
:PermDeleteAccount odrl:duty [
|
|
161
|
+
odrl:action odrl:inform
|
|
162
|
+
] .
|
|
163
|
+
}.
|
|
164
|
+
|
|
165
|
+
?clause :clauseId ?cid ; :text ?txt .
|
|
166
|
+
|
|
167
|
+
(90 ?w) math:sum ?raw .
|
|
168
|
+
( :Agreement1 :ConsumerExample :DeleteAccountNoSafeguards ?cid ) log:skolem ?risk .
|
|
169
|
+
( :Agreement1 :ConsumerExample :DeleteAccountNoSafeguardsSource ?cid ) log:skolem ?src .
|
|
170
|
+
|
|
171
|
+
( "Risk: account/data removal is permitted without notice safeguards (no notice constraint and no duty to inform). Clause %s: %s"
|
|
172
|
+
?cid ?txt ) string:format ?why .
|
|
173
|
+
}
|
|
174
|
+
=>
|
|
175
|
+
{
|
|
176
|
+
?src a risk:RiskSource, risk:LegalComplianceRisk ;
|
|
177
|
+
dct:source :PermDeleteAccount ;
|
|
178
|
+
dct:description "Account removal permitted without notice safeguards." .
|
|
179
|
+
|
|
180
|
+
?risk a dpv:Risk, risk:UnwantedDataDeletion, risk:DataUnavailable, risk:DataErasureError, risk:DataLoss ;
|
|
181
|
+
dct:source :PermDeleteAccount ;
|
|
182
|
+
risk:hasRiskSource ?src ;
|
|
183
|
+
dpv:hasConsequence risk:DataLoss, risk:DataUnavailable, risk:CustomerConfidenceLoss ;
|
|
184
|
+
dpv:hasImpact risk:FinancialLoss, risk:NonMaterialDamage ;
|
|
185
|
+
:aboutClause ?clause ;
|
|
186
|
+
:scoreRaw ?raw ;
|
|
187
|
+
:violatesNeed :Need_DataCannotBeRemoved ;
|
|
188
|
+
dct:description ?why .
|
|
189
|
+
|
|
190
|
+
:ProcessContext1 dpv:hasRisk ?risk .
|
|
191
|
+
|
|
192
|
+
( ?risk :M1 ) log:skolem ?m1 .
|
|
193
|
+
( ?risk :M2 ) log:skolem ?m2 .
|
|
194
|
+
|
|
195
|
+
?m1 a dpv:RiskMitigationMeasure ;
|
|
196
|
+
dct:description "Add a notice constraint (minimum noticeDays) before account removal." ;
|
|
197
|
+
dpv:mitigatesRisk ?risk ;
|
|
198
|
+
:suggestAdd {
|
|
199
|
+
:PermDeleteAccount odrl:constraint [
|
|
200
|
+
a odrl:Constraint ;
|
|
201
|
+
odrl:leftOperand tosl:noticeDays ;
|
|
202
|
+
odrl:operator odrl:gteq ;
|
|
203
|
+
odrl:rightOperand "14"^^xsd:integer
|
|
204
|
+
] .
|
|
205
|
+
} .
|
|
206
|
+
|
|
207
|
+
?m2 a dpv:RiskMitigationMeasure ;
|
|
208
|
+
dct:description "Add a duty to inform the consumer prior to account removal." ;
|
|
209
|
+
dpv:mitigatesRisk ?risk ;
|
|
210
|
+
:suggestAdd {
|
|
211
|
+
:PermDeleteAccount odrl:duty [
|
|
212
|
+
a odrl:Duty ;
|
|
213
|
+
odrl:action odrl:inform
|
|
214
|
+
] .
|
|
215
|
+
} .
|
|
216
|
+
|
|
217
|
+
?risk dpv:isMitigatedByMeasure ?m1, ?m2 .
|
|
218
|
+
} .
|
|
219
|
+
|
|
220
|
+
# R2: change terms noticeDays is below consumer requirement
|
|
221
|
+
{
|
|
222
|
+
:Agreement1 :policyGraph ?G .
|
|
223
|
+
:ConsumerExample :hasNeed :Need_ChangeOnlyWithPriorNotice .
|
|
224
|
+
:Need_ChangeOnlyWithPriorNotice :importance ?w ; :minNoticeDays ?req .
|
|
225
|
+
|
|
226
|
+
?G log:includes {
|
|
227
|
+
:PermChangeTerms a odrl:Permission ;
|
|
228
|
+
odrl:action tosl:changeTerms ;
|
|
229
|
+
:clause ?clause ;
|
|
230
|
+
odrl:duty [
|
|
231
|
+
odrl:action odrl:inform ;
|
|
232
|
+
odrl:constraint [
|
|
233
|
+
odrl:leftOperand tosl:noticeDays ;
|
|
234
|
+
odrl:rightOperand ?days
|
|
235
|
+
]
|
|
236
|
+
] .
|
|
237
|
+
}.
|
|
238
|
+
|
|
239
|
+
?days math:lessThan ?req .
|
|
240
|
+
?clause :clauseId ?cid ; :text ?txt .
|
|
241
|
+
|
|
242
|
+
(70 ?w) math:sum ?raw .
|
|
243
|
+
( :Agreement1 :ConsumerExample :NoticeTooShort ?cid ) log:skolem ?risk .
|
|
244
|
+
( :Agreement1 :ConsumerExample :NoticeTooShortSource ?cid ) log:skolem ?src .
|
|
245
|
+
|
|
246
|
+
( "Risk: terms may change with notice (%s days) below consumer requirement (%s days). Clause %s: %s"
|
|
247
|
+
?days ?req ?cid ?txt ) string:format ?why .
|
|
248
|
+
}
|
|
249
|
+
=>
|
|
250
|
+
{
|
|
251
|
+
?src a risk:RiskSource, risk:PolicyRisk ;
|
|
252
|
+
dct:source :PermChangeTerms ;
|
|
253
|
+
dct:description "Notice for changing terms is shorter than consumer requirement." .
|
|
254
|
+
|
|
255
|
+
?risk a dpv:Risk, risk:PolicyRisk, risk:CustomerConfidenceLoss ;
|
|
256
|
+
dct:source :PermChangeTerms ;
|
|
257
|
+
risk:hasRiskSource ?src ;
|
|
258
|
+
dpv:hasConsequence risk:CustomerConfidenceLoss ;
|
|
259
|
+
dpv:hasImpact risk:NonMaterialDamage ;
|
|
260
|
+
:aboutClause ?clause ;
|
|
261
|
+
:scoreRaw ?raw ;
|
|
262
|
+
:violatesNeed :Need_ChangeOnlyWithPriorNotice ;
|
|
263
|
+
dct:description ?why .
|
|
264
|
+
|
|
265
|
+
:ProcessContext1 dpv:hasRisk ?risk .
|
|
266
|
+
|
|
267
|
+
( ?risk :M1 ) log:skolem ?m1 .
|
|
268
|
+
?m1 a dpv:RiskMitigationMeasure ;
|
|
269
|
+
dct:description "Increase minimum noticeDays in the inform duty to meet the consumer requirement." ;
|
|
270
|
+
dpv:mitigatesRisk ?risk ;
|
|
271
|
+
:suggestAdd {
|
|
272
|
+
:PermChangeTerms odrl:duty [
|
|
273
|
+
a odrl:Duty ;
|
|
274
|
+
odrl:action odrl:inform ;
|
|
275
|
+
odrl:constraint [
|
|
276
|
+
a odrl:Constraint ;
|
|
277
|
+
odrl:leftOperand tosl:noticeDays ;
|
|
278
|
+
odrl:operator odrl:gteq ;
|
|
279
|
+
odrl:rightOperand "14"^^xsd:integer
|
|
280
|
+
]
|
|
281
|
+
] .
|
|
282
|
+
} .
|
|
283
|
+
|
|
284
|
+
?risk dpv:isMitigatedByMeasure ?m1 .
|
|
285
|
+
} .
|
|
286
|
+
|
|
287
|
+
# R3: share data WITHOUT explicit consent constraint
|
|
288
|
+
{
|
|
289
|
+
:Agreement1 :policyGraph ?G .
|
|
290
|
+
:ConsumerExample :hasNeed :Need_NoSharingWithoutConsent .
|
|
291
|
+
:Need_NoSharingWithoutConsent :importance ?w .
|
|
292
|
+
|
|
293
|
+
?G log:includes {
|
|
294
|
+
:PermShareData a odrl:Permission ;
|
|
295
|
+
odrl:action tosl:shareData ;
|
|
296
|
+
:clause ?clause .
|
|
297
|
+
}.
|
|
298
|
+
|
|
299
|
+
?G log:notIncludes {
|
|
300
|
+
:PermShareData odrl:constraint [
|
|
301
|
+
odrl:leftOperand tosl:consent ;
|
|
302
|
+
odrl:operator odrl:eq ;
|
|
303
|
+
odrl:rightOperand true
|
|
304
|
+
] .
|
|
305
|
+
}.
|
|
306
|
+
|
|
307
|
+
?clause :clauseId ?cid ; :text ?txt .
|
|
308
|
+
|
|
309
|
+
(85 ?w) math:sum ?raw .
|
|
310
|
+
( :Agreement1 :ConsumerExample :ShareNoConsent ?cid ) log:skolem ?risk .
|
|
311
|
+
( :Agreement1 :ConsumerExample :ShareNoConsentSource ?cid ) log:skolem ?src .
|
|
312
|
+
|
|
313
|
+
( "Risk: user data sharing is permitted without an explicit consent constraint. Clause %s: %s"
|
|
314
|
+
?cid ?txt ) string:format ?why .
|
|
315
|
+
}
|
|
316
|
+
=>
|
|
317
|
+
{
|
|
318
|
+
?src a risk:RiskSource, risk:PolicyRisk ;
|
|
319
|
+
dct:source :PermShareData ;
|
|
320
|
+
dct:description "Data sharing permitted without explicit consent constraint." .
|
|
321
|
+
|
|
322
|
+
?risk a dpv:Risk, risk:UnwantedDisclosureData, risk:CustomerConfidenceLoss ;
|
|
323
|
+
dct:source :PermShareData ;
|
|
324
|
+
risk:hasRiskSource ?src ;
|
|
325
|
+
dpv:hasConsequence risk:CustomerConfidenceLoss ;
|
|
326
|
+
dpv:hasImpact risk:NonMaterialDamage, risk:FinancialLoss ;
|
|
327
|
+
:aboutClause ?clause ;
|
|
328
|
+
:scoreRaw ?raw ;
|
|
329
|
+
:violatesNeed :Need_NoSharingWithoutConsent ;
|
|
330
|
+
dct:description ?why .
|
|
331
|
+
|
|
332
|
+
:ProcessContext1 dpv:hasRisk ?risk .
|
|
333
|
+
|
|
334
|
+
( ?risk :M1 ) log:skolem ?m1 .
|
|
335
|
+
?m1 a dpv:RiskMitigationMeasure ;
|
|
336
|
+
dct:description "Add an explicit consent constraint before data sharing." ;
|
|
337
|
+
dpv:mitigatesRisk ?risk ;
|
|
338
|
+
:suggestAdd {
|
|
339
|
+
:PermShareData odrl:constraint [
|
|
340
|
+
a odrl:Constraint ;
|
|
341
|
+
odrl:leftOperand tosl:consent ;
|
|
342
|
+
odrl:operator odrl:eq ;
|
|
343
|
+
odrl:rightOperand true
|
|
344
|
+
] .
|
|
345
|
+
} .
|
|
346
|
+
|
|
347
|
+
?risk dpv:isMitigatedByMeasure ?m1 .
|
|
348
|
+
} .
|
|
349
|
+
|
|
350
|
+
# R4: prohibit export (no portability)
|
|
351
|
+
{
|
|
352
|
+
:Agreement1 :policyGraph ?G .
|
|
353
|
+
:ConsumerExample :hasNeed :Need_DataPortability .
|
|
354
|
+
:Need_DataPortability :importance ?w .
|
|
355
|
+
|
|
356
|
+
?G log:includes {
|
|
357
|
+
:ProhibitExportData a odrl:Prohibition ;
|
|
358
|
+
odrl:action tosl:exportData ;
|
|
359
|
+
:clause ?clause .
|
|
360
|
+
}.
|
|
361
|
+
|
|
362
|
+
?clause :clauseId ?cid ; :text ?txt .
|
|
363
|
+
|
|
364
|
+
(60 ?w) math:sum ?raw .
|
|
365
|
+
( :Agreement1 :ConsumerExample :NoPortability ?cid ) log:skolem ?risk .
|
|
366
|
+
( :Agreement1 :ConsumerExample :NoPortabilitySource ?cid ) log:skolem ?src .
|
|
367
|
+
|
|
368
|
+
( "Risk: portability is restricted because exporting user data is prohibited. Clause %s: %s"
|
|
369
|
+
?cid ?txt ) string:format ?why .
|
|
370
|
+
}
|
|
371
|
+
=>
|
|
372
|
+
{
|
|
373
|
+
?src a risk:RiskSource, risk:PolicyRisk ;
|
|
374
|
+
dct:source :ProhibitExportData ;
|
|
375
|
+
dct:description "Data export is prohibited, reducing portability." .
|
|
376
|
+
|
|
377
|
+
?risk a dpv:Risk, risk:PolicyRisk, risk:CustomerConfidenceLoss ;
|
|
378
|
+
dct:source :ProhibitExportData ;
|
|
379
|
+
risk:hasRiskSource ?src ;
|
|
380
|
+
dpv:hasConsequence risk:CustomerConfidenceLoss ;
|
|
381
|
+
dpv:hasImpact risk:NonMaterialDamage ;
|
|
382
|
+
:aboutClause ?clause ;
|
|
383
|
+
:scoreRaw ?raw ;
|
|
384
|
+
:violatesNeed :Need_DataPortability ;
|
|
385
|
+
dct:description ?why .
|
|
386
|
+
|
|
387
|
+
:ProcessContext1 dpv:hasRisk ?risk .
|
|
388
|
+
|
|
389
|
+
( ?risk :M1 ) log:skolem ?m1 .
|
|
390
|
+
?m1 a dpv:RiskMitigationMeasure ;
|
|
391
|
+
dct:description "Add a permission allowing data export (or remove the prohibition) to support portability." ;
|
|
392
|
+
dpv:mitigatesRisk ?risk ;
|
|
393
|
+
:suggestAdd {
|
|
394
|
+
:Policy1 odrl:permission [
|
|
395
|
+
a odrl:Permission ;
|
|
396
|
+
odrl:assigner :Provider ;
|
|
397
|
+
odrl:assignee odrl:consumer ;
|
|
398
|
+
odrl:action tosl:exportData ;
|
|
399
|
+
odrl:target :UserData
|
|
400
|
+
] .
|
|
401
|
+
} .
|
|
402
|
+
|
|
403
|
+
?risk dpv:isMitigatedByMeasure ?m1 .
|
|
404
|
+
} .
|
|
405
|
+
|
|
406
|
+
# ------------------------------------------------
|
|
407
|
+
# 4) Score normalization + DPV-RISK severity/level
|
|
408
|
+
# ------------------------------------------------
|
|
409
|
+
|
|
410
|
+
{ ?r a dpv:Risk ; :scoreRaw ?raw . ?raw math:greaterThan "100"^^xsd:integer . }
|
|
411
|
+
=> { ?r :score "100"^^xsd:integer . } .
|
|
412
|
+
|
|
413
|
+
{ ?r a dpv:Risk ; :scoreRaw ?raw . "100"^^xsd:integer math:notLessThan ?raw . }
|
|
414
|
+
=> { ?r :score ?raw . } .
|
|
415
|
+
|
|
416
|
+
{ ?r a dpv:Risk ; :score ?s . ?s math:greaterThan "79"^^xsd:integer . }
|
|
417
|
+
=> { ?r dpv:hasSeverity risk:HighSeverity ; dpv:hasRiskLevel risk:HighRisk . } .
|
|
418
|
+
|
|
419
|
+
{ ?r a dpv:Risk ; :score ?s . ?s math:lessThan "80"^^xsd:integer . ?s math:greaterThan "49"^^xsd:integer . }
|
|
420
|
+
=> { ?r dpv:hasSeverity risk:ModerateSeverity ; dpv:hasRiskLevel risk:ModerateRisk . } .
|
|
421
|
+
|
|
422
|
+
{ ?r a dpv:Risk ; :score ?s . ?s math:lessThan "50"^^xsd:integer . }
|
|
423
|
+
=> { ?r dpv:hasSeverity risk:LowSeverity ; dpv:hasRiskLevel risk:LowRisk . } .
|
|
424
|
+
|
|
425
|
+
# ------------------------------------------------------------------------------
|
|
426
|
+
# 5) Ranked explainable output (Eyeling -r prints these in key order)
|
|
427
|
+
# ------------------------------------------------------------------------------
|
|
428
|
+
|
|
429
|
+
# Header
|
|
430
|
+
{
|
|
431
|
+
:Agreement1 dct:title ?alabel .
|
|
432
|
+
:ConsumerExample dct:title ?plabel .
|
|
433
|
+
( "\n=== Ranked DPV Risk Report ===\nAgreement: %s\nProfile: %s\n\n"
|
|
434
|
+
?alabel ?plabel ) string:format ?hdr .
|
|
435
|
+
}
|
|
436
|
+
=>
|
|
437
|
+
{
|
|
438
|
+
( :Agreement1 :ConsumerExample 0 ) log:outputString ?hdr .
|
|
439
|
+
} .
|
|
440
|
+
|
|
441
|
+
# Risk lines (key includes inverse score = 1000 - score)
|
|
442
|
+
{
|
|
443
|
+
?r a dpv:Risk ;
|
|
444
|
+
:score ?score ;
|
|
445
|
+
dpv:hasRiskLevel ?lvl ;
|
|
446
|
+
dpv:hasSeverity ?sev ;
|
|
447
|
+
:aboutClause ?clause ;
|
|
448
|
+
dct:description ?why .
|
|
449
|
+
?clause :clauseId ?cid .
|
|
450
|
+
|
|
451
|
+
( "1000"^^xsd:integer ?score ) math:difference ?inv .
|
|
452
|
+
|
|
453
|
+
( "score=%s (%s, %s) clause %s\n %s\n\n"
|
|
454
|
+
?score ?lvl ?sev ?cid ?why ) string:format ?line .
|
|
455
|
+
}
|
|
456
|
+
=>
|
|
457
|
+
{
|
|
458
|
+
( :Agreement1 :ConsumerExample 1 ?inv ?r ) log:outputString ?line .
|
|
459
|
+
} .
|
|
460
|
+
|
|
461
|
+
# Mitigation lines (same ordering as their risk)
|
|
462
|
+
{
|
|
463
|
+
?r a dpv:Risk ;
|
|
464
|
+
:score ?score ;
|
|
465
|
+
dpv:isMitigatedByMeasure ?m ;
|
|
466
|
+
:aboutClause ?clause .
|
|
467
|
+
?clause :clauseId ?cid .
|
|
468
|
+
?m dct:description ?md .
|
|
469
|
+
|
|
470
|
+
( "1000"^^xsd:integer ?score ) math:difference ?inv .
|
|
471
|
+
|
|
472
|
+
( " - mitigation for clause %s: %s\n"
|
|
473
|
+
?cid ?md ) string:format ?mline .
|
|
474
|
+
}
|
|
475
|
+
=>
|
|
476
|
+
{
|
|
477
|
+
( :Agreement1 :ConsumerExample 2 ?inv ?r ) log:outputString ?mline .
|
|
478
|
+
} .
|
|
479
|
+
|