OILS / spec / ysh-purity.test.sh View on Github | oils.pub

407 lines, 179 significant
1## oils_failures_allowed: 10
2
3# Not disallowed:
4# setglobal, mutating arguments with setvar
5
6#### eval() is a pure function
7shopt --set ysh:upgrade
8
9var pure = ^(
10 const a = 1
11 const b = 2
12)
13
14var d = eval(pure, to_dict=true)
15pp test_ (d)
16
17var impure = ^(seq 3 | wc -l)
18
19try {
20 call eval(impure)
21}
22#= _error
23echo impure code=$[_error.code]
24
25# Can run impure code after pure code
26call io->eval(impure)
27
28## STDOUT:
29(Dict) {"a":1,"b":2}
30impure code=5
313
32## END
33
34#### evalExpr() is a pure function
35shopt --set ysh:upgrade
36
37var x = 42
38var pure = ^[x + 1]
39echo pure=$[evalExpr(pure)]
40
41var impure = ^[x + $(echo 3)]
42try {
43 echo impure code=$[evalExpr(impure)]
44}
45echo impure code=$[_error.code]
46
47# Can run impure code after pure code
48echo impure=$[io->evalExpr(impure)]
49
50## STDOUT:
51pure=43
52impure code=5
53impure=45
54## END
55
56#### Idiom to handle purity errors from untrusted config files
57
58echo "TODO: what's the idiom?"
59
60# trap PURE ? Is this like trap ERR?
61# You can handle these errors
62#
63# OILS_CRASH_DUMP_DIR=?
64#
65# Or do you need 2 more flags?
66#
67# --eval-pure-str 'try { source user-config.ysh }'
68# --eval-str
69#
70# That is a bit annoying?
71#
72# --eval pure:foo.hay
73# --eval-str 'pure:echo hi'
74# --eval any:foo.hay
75# --eval-str 'any:echo hi'
76
77## STDOUT:
78## END
79
80#### Executor: can run user-defined Procs
81shopt --set ysh:upgrade
82
83var g = []
84
85proc p-outside {
86 # note: append builtin would be nice
87 call g->append('p-outside')
88}
89
90# The Hay file can call any procs? That seems wrong actually
91# We want to hide some of them
92
93var cmd = ^(
94 p-outside
95
96 proc p-inside {
97 call g->append('p-inside')
98 }
99 p-inside
100)
101
102call eval(cmd)
103
104pp test_ (g)
105
106## STDOUT:
107(List) ["p-outside","p-inside"]
108## END
109
110#### Executor: can run Hay (while Hay is hard-coded)
111
112shopt --set ysh:upgrade
113
114hay define Package/INSTALL
115
116var cmd = ^(
117 Package foo {
118 version = '1.1'
119 INSTALL { echo hi }
120 }
121)
122
123call eval(cmd)
124
125json write (_hay().children[0].attrs)
126
127## STDOUT:
128{
129 "version": "1.1"
130}
131## END
132
133
134#### Executor: External Commands not allowed
135
136var cmd = ^(seq 3)
137
138call io->eval(cmd)
139
140call eval(cmd)
141
142## status: 127
143## STDOUT:
1441
1452
1463
147## END
148
149
150#### Command subs, pipelines not allowed with --eval-pure
151
152echo >command-sub.sh 'x=$(echo command sub)'
153echo >command-sub.ysh 'var x = $(echo command sub)'
154
155$SH --eval command-sub.sh -c 'echo $x'
156$SH --eval-pure command-sub.sh -c 'echo command-sub.sh=$?'
157$SH --eval-pure command-sub.ysh -c 'echo command-sub.ysh=$?'
158
159echo
160
161echo >pipeline.sh 'seq 3 | wc -l'
162
163$SH --eval pipeline.sh -c 'echo eval'
164$SH --eval-pure pipeline.sh -c 'echo pipeline.sh=$?'
165
166
167## status: 0
168## STDOUT:
169command sub
170command-sub.sh=5
171command-sub.ysh=5
172
1733
174eval
175pipeline.sh=5
176## END
177
178#### Process subs, subshells not allowed with eval()
179shopt --set ysh:upgrade
180
181var cmd = ^( cat <(echo 1) <(echo 2) )
182call io->eval(cmd)
183
184try {
185 call eval(cmd)
186}
187echo code=$[_error.code] message=$[_error.message]
188echo
189
190var cmd = ^(( echo subshell ) )
191call io->eval(cmd)
192
193try {
194 call eval(cmd)
195}
196echo code=$[_error.code] message=$[_error.message]
197
198## STDOUT:
1991
2002
201code=5 message=Process subs aren't allowed in pure mode (OILS-ERR-204)
202
203subshell
204code=5 message=Subshells aren't allowed in pure mode (OILS-ERR-204)
205## END
206
207#### Background job &
208shopt --set ysh:upgrade
209
210var cmd = ^( sleep 0.01 & wait )
211call io->eval(cmd)
212
213try {
214 call eval(cmd)
215}
216echo code=$[_error.code] message=$[_error.message]
217
218var cmd = ^( seq 3 | wc -l )
219call io->eval(cmd)
220
221try {
222 call eval(cmd)
223}
224echo code=$[_error.code] message=$[_error.message]
225
226## STDOUT:
227code=5 message=Background jobs aren't allowed in pure mode (OILS-ERR-204)
2283
229code=5 message=Pipelines aren't allowed in pure mode (OILS-ERR-204)
230## END
231
232#### Redirects
233shopt --set ysh:upgrade
234
235hay define Package
236var cmd = ^( Package foo > out.txt )
237call io->eval(cmd)
238rm -v out.txt
239
240try {
241 call eval(cmd)
242}
243# not created, but there's also no error?
244# oh it's because of the PureExecutor
245
246## STDOUT:
247TODO
248## END
249
250#### Are any builtins allowed? true, false
251shopt --set ysh:upgrade
252
253
254# what other builtins should be allowed?
255# - set and shopt could be dangerous?
256# - set -- 1 2 3 may be OK
257# - test -n is safe, but test --file is not
258# - YSH mostly won't need it
259# - not part of YSH
260# - unset
261# - printf -v (otherwise printf does I/O)
262# - shift - use ARGV
263# - getopts
264# - alias
265# Other:
266# - type - some of this does I/O
267#
268# If we only consider YSH, everything has a trivial replacement, e.g. true and
269# false. false can be assert [false]
270
271var cmd = ^(
272 true
273 echo true
274 builtin true
275 echo builtin true
276 command true
277 echo command true
278
279 builtin false
280 echo builtin false
281)
282
283call io->eval(cmd)
284call eval(cmd)
285echo
286
287## STDOUT:
288true
289builtin true
290command true
291## END
292
293#### Are source or use builtins allowed?
294shopt --set ysh:upgrade
295
296# Problem: they cna "steal" information with directory traversal attacks?
297# maybe only allow them in the same dirs
298#
299# Or maybe have a $PATH - $OILS_LIB_PATH
300# and it can only be set by the caller, via command line flag?
301#
302# ysh --oils-path dir1:dir2 --eval
303#
304# use foo.ysh # relative to the path
305
306var cmd = ^(
307 source foo.ysh
308 use foo.ysh
309)
310
311call eval (cmd)
312
313## STDOUT:
314## END
315
316#### Can log to stderr in pure mode
317shopt --set ysh:upgrade
318
319var cmd = ^(
320 var name = 'world'
321
322 # I think this should be allowed
323 # And maybe log can be customized with:
324 # - xtrace unification? hierarchy
325 # - timestamps
326
327 log "hi $name"
328)
329
330call io->eval(cmd)
331call eval (cmd)
332
333## STDOUT:
334## END
335
336
337#### io and vm are not allowed
338
339var cmd = ^(
340 = vm.getFrame(-1)
341 = vm.id({})
342
343 = io.stdin
344)
345
346call io->eval(cmd)
347call eval (cmd)
348
349## STDOUT:
350## END
351
352#### Can't make an alias of io->eval and call it, etc.
353shopt --set ysh:upgrade
354
355# The --eval-pure could be make an alias, and then "trick" the post-amble into
356# calling it.
357
358var f = io->eval
359
360var cmd = ^(echo hi)
361
362call f(cmd)
363
364## STDOUT:
365## END
366
367#### Globbing not allowed
368
369# TODO: should be @[io.glob('*.txt')]
370# That is a bit verbose
371var cmd = ^(
372 echo *.txt
373)
374
375call io->eval(cmd)
376call eval(cmd)
377
378## STDOUT:
379## END
380
381#### $RANDOM $SECONDS
382shopt --set ysh:upgrade
383
384var cmd = ^(
385 echo not-implemented=$RANDOM
386 echo $SECONDS
387)
388
389call io->eval(cmd)
390call eval(cmd)
391
392## STDOUT:
393## END
394
395#### Purely-evaluated code can't set traps for later
396
397# this follows from 'no builtins', but probably good to test
398
399var cmd = ^(
400 trap 'echo INT' INT
401)
402
403call io->eval(cmd)
404call eval(cmd)
405
406## STDOUT:
407## END