Skip to content

Commit 9564713

Browse files
ThomasHickmantetron
authored andcommitted
Add javascript code snippet validation (#662)
* Add validation for javascript expressions. Fixes #594. To do this, it is needed to add jshint and refractor js_sandbox to so that it can be useful for this.
1 parent b1f9024 commit 9564713

13 files changed

+25040
-67
lines changed

MANIFEST.in

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ include cwltool/schemas/v1.1.0-dev1/salad/schema_salad/metaschema/*.yml
1919
include cwltool/schemas/v1.1.0-dev1/salad/schema_salad/metaschema/*.md
2020
include cwltool/cwlNodeEngine.js
2121
include cwltool/cwlNodeEngineJSConsole.js
22+
include cwltool/cwlNodeEngineWithContext.js
2223
include cwltool/extensions.yml
24+
include cwltool/jshint/jshint_wrapper.js
25+
include cwltool/jshint/jshint.js
2326
global-exclude *~
2427
global-exclude *.pyc

appveyor.yml

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
version: .{build}-{branch}
22

33
environment:
4+
nodejs_version: "8"
5+
46
SYSTEMROOT: "C:\\WINDOWS"
57

68
matrix:
@@ -22,18 +24,18 @@ environment:
2224

2325

2426
install:
27+
# Get the latest stable version of Node.js or io.js
28+
- ps: Install-Product node $env:nodejs_version
2529
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
26-
- "python --version"
27-
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
2830

2931
build_script:
30-
- "%CMD_IN_ENV% python -m pip install -U setuptools"
32+
- "%CMD_IN_ENV% python -m pip install -U setuptools pip"
33+
- "%CMD_IN_ENV% pip install pytest mock"
3134
- "%CMD_IN_ENV% pip install ."
3235

3336

3437
test_script:
35-
36-
- "%CMD_IN_ENV% python setup.py test"
38+
- "%CMD_IN_ENV% py.test --verbose -p no:cacheprovider"
3739

3840
branches:
3941
only:

cwltool/argparser.py

+5
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ def arg_parser(): # type: () -> argparse.ArgumentParser
152152
"timestamps to the errors, warnings, and "
153153
"notifications.")
154154
parser.add_argument("--js-console", action="store_true", help="Enable javascript console output")
155+
parser.add_argument("--disable-js-validation", action="store_true", help="Disable javascript validation.")
156+
parser.add_argument("--js-hint-options-file",
157+
type=Text,
158+
help="File of options to pass to jshint."
159+
"This includes the added option \"includewarnings\". ")
155160
dockergroup = parser.add_mutually_exclusive_group()
156161
dockergroup.add_argument("--user-space-docker-cmd", metavar="CMD",
157162
help="(Linux/OS X only) Specify a user space docker "

cwltool/cwlNodeEngineWithContext.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use strict";
2+
process.stdin.setEncoding("utf8");
3+
var incoming = "";
4+
var firstInput = true;
5+
var context = {};
6+
7+
process.stdin.on("data", function(chunk) {
8+
incoming += chunk;
9+
var i = incoming.indexOf("\n");
10+
while (i > -1) {
11+
try{
12+
var input = incoming.substr(0, i);
13+
incoming = incoming.substr(i+1);
14+
var fn = JSON.parse(input);
15+
if(firstInput){
16+
context = require("vm").runInNewContext(fn, {});
17+
}
18+
else{
19+
process.stdout.write(JSON.stringify(require("vm").runInNewContext(fn, context)) + "\n");
20+
}
21+
}
22+
catch(e){
23+
console.error(e);
24+
}
25+
if(firstInput){
26+
firstInput = false;
27+
}
28+
else{
29+
/*strings to indicate the process has finished*/
30+
console.log("r1cepzbhUTxtykz5XTC4");
31+
console.error("r1cepzbhUTxtykz5XTC4");
32+
}
33+
34+
i = incoming.indexOf("\n");
35+
}
36+
});
37+
process.stdin.on("end", process.exit);

cwltool/jshint/jshint.js

+24,568
Large diffs are not rendered by default.

cwltool/jshint/jshint_wrapper.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"use strict";
2+
// set a global object, in order for jshint to work
3+
var global = this;
4+
5+
function validateJS(input) {
6+
var jshintGlobalsObj = {};
7+
input.globals.forEach(function (global) {
8+
jshintGlobalsObj[global] = true;
9+
})
10+
var includewarnings;
11+
12+
if (input.options.includewarnings !== undefined) {
13+
includewarnings = input.options.includewarnings;
14+
delete input.options.includewarnings;
15+
}
16+
17+
JSHINT(
18+
input.code,
19+
input.options,
20+
jshintGlobalsObj
21+
)
22+
23+
var jshintData = JSHINT.data();
24+
if (jshintData.errors !== undefined) {
25+
if (includewarnings !== undefined) {
26+
jshintData.errors = jshintData.errors.filter(function (error) {
27+
return includewarnings.indexOf(error.code) !== -1 || error.code[0] == "E";
28+
})
29+
}
30+
31+
jshintData.errors.forEach(function (error) {
32+
if (error.code == "W104" || error.code == "W119") {
33+
if (error.code == "W104"){
34+
var jslint_suffix = " (use 'esversion: 6') or Mozilla JS extensions (use moz)."
35+
}
36+
else{
37+
var jslint_suffix = " (use 'esversion: 6')"
38+
}
39+
40+
error.reason = error.reason.slice(0, -jslint_suffix.length - 1) +
41+
". CWL only supports ES5.1";
42+
}
43+
})
44+
}
45+
46+
return jshintData;
47+
}

cwltool/load_tool.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def validate_document(document_loader, # type: Loader
187187
fetcher_constructor=None, # type: FetcherConstructorType
188188
skip_schemas=None, # type: bool
189189
overrides=None, # type: List[Dict]
190-
metadata=None, # type: Optional[Dict]
190+
metadata=None # type: Optional[Dict]
191191
):
192192
# type: (...) -> Tuple[Loader, Names, Union[Dict[Text, Any], List[Dict[Text, Any]]], Dict[Text, Any], Text]
193193
"""Validate a CWL document."""

cwltool/process.py

+14
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from schema_salad.sourceline import SourceLine
3131
from six.moves import urllib
3232

33+
from .validate_js import validate_js_expressions
3334
from .utils import cmp_like_py2
3435
from .builder import Builder
3536
from .errors import UnsupportedRequirement, WorkflowException
@@ -548,6 +549,19 @@ def __init__(self, toolpath_object, **kwargs):
548549
(Text(e), self.tool["id"],
549550
json.dumps(self.outputs_record_schema, indent=4)))
550551

552+
if toolpath_object.get("class") is not None and not kwargs.get("disable_js_validation", False):
553+
if kwargs.get("js_hint_options_file") is not None:
554+
try:
555+
with open(kwargs["js_hint_options_file"]) as options_file:
556+
validate_js_options = json.load(options_file)
557+
except (OSError, ValueError) as e:
558+
_logger.error("Failed to read options file %s" % kwargs["js_hint_options_file"])
559+
raise e
560+
else:
561+
validate_js_options = None
562+
563+
validate_js_expressions(cast(CommentedMap, toolpath_object), self.doc_schema.names[toolpath_object["class"]], validate_js_options)
564+
551565
def _init_job(self, joborder, **kwargs):
552566
# type: (Dict[Text, Text], **Any) -> Builder
553567
"""

0 commit comments

Comments
 (0)