diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d62bb8b..19cc367 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -73,3 +73,71 @@ jobs: tags: | ghcr.io/debricked/vulnerable-functionality:gradle debricked/vulnerable-functionality:gradle + + docker-javascript: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: javascript/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ghcr.io/debricked/vulnerable-functionality:javascript + debricked/vulnerable-functionality:javascript + + docker-golang: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: golang/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ghcr.io/debricked/vulnerable-functionality:golang + debricked/vulnerable-functionality:golang diff --git a/.github/workflows/golang.yml b/.github/workflows/golang.yml new file mode 100644 index 0000000..cb9ecb7 --- /dev/null +++ b/.github/workflows/golang.yml @@ -0,0 +1,31 @@ +name: Golang test + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: '3.8' + - name: Set up Go 1.16.5 + uses: actions/setup-go@v2 + with: + go-version: '1.16.5' + - name: Display Golang version + run: go version + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + go get -u golang.org/x/tools/cmd/callgraph + cd golang/test && go install + - name: Test with pytest + run: | + pip install pytest + pytest golang/test/go_cg_test.py diff --git a/.github/workflows/javascript.yml b/.github/workflows/javascript.yml new file mode 100644 index 0000000..5fc6ea2 --- /dev/null +++ b/.github/workflows/javascript.yml @@ -0,0 +1,27 @@ +name: JavaScript test + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: '3.8' + - name: Display Python version + run: python -c "import sys; print(sys.version)" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r javascript/src/requirements.txt + sudo npm install -g @persper/js-callgraph + sudo apt-get install dos2unix + - name: Test with pytest + run: | + pip install pytest + pytest diff --git a/.github/workflows/vulnfunc_go.yml b/.github/workflows/vulnfunc_go.yml new file mode 100644 index 0000000..29ef544 --- /dev/null +++ b/.github/workflows/vulnfunc_go.yml @@ -0,0 +1,14 @@ +name: Vulnerable Functionality test golang + +on: [push] + +jobs: + vulnfunc: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: TeodorBucht1729/vulnerable-functionality/golang@go-vuln-func + with: + root-mainpackage-directory: 'golang/test' + diff --git a/.github/workflows/vulnfunc_js.yml b/.github/workflows/vulnfunc_js.yml new file mode 100644 index 0000000..85ba7e7 --- /dev/null +++ b/.github/workflows/vulnfunc_js.yml @@ -0,0 +1,14 @@ +name: Vulnerable Functionality test javascript + +on: [push] + +jobs: + vulnfunc: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: debricked/vulnerable-functionality/javascript@master + with: + root-packagejson-directory: 'javascript/test/fresh_install' + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c0c561 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +.pytest_cache/ + +# results from generating js callgraph +final_cg.json +partial_cg.json +cg.json \ No newline at end of file diff --git a/java/common/commonWrapper.sh b/common/commonWrapper.sh similarity index 100% rename from java/common/commonWrapper.sh rename to common/commonWrapper.sh diff --git a/golang/Dockerfile b/golang/Dockerfile new file mode 100644 index 0000000..a8190ca --- /dev/null +++ b/golang/Dockerfile @@ -0,0 +1,12 @@ +# syntax=docker/dockerfile:1 + +FROM golang:1.16 + +COPY ./src /vulnfunc/golang/src + +RUN apt-get update ; apt-get install --no-install-recommends -y zip +RUN add-apt-repository ppa:deadsnakes/ppa ; apt-get update ; apt-get install --no-install-recommends -y python3 +RUN go get -u golang.org/x/tools/cmd/callgraph + +COPY ./entrypoint.sh /vulnfunc/golang/entrypoint.sh +RUN chmod +x /vulnfunc/golang/entrypoint.sh \ No newline at end of file diff --git a/golang/action.yml b/golang/action.yml new file mode 100644 index 0000000..3c24a02 --- /dev/null +++ b/golang/action.yml @@ -0,0 +1,18 @@ +name: Debricked Vulnerable Functionality for Golang +author: Debricked +description: Calculates the call graph for a Golang main package +inputs: + root-mainpackage-directory: + description: Directory containing the .go with the main package and function. + required: true + +runs: + using: docker + # image: docker://debricked/vulnerable-functionality:golang + image: Dockerfile + entrypoint: /vulnfunc/golang/entrypoint.sh + args: + - ${{ inputs.root-mainpackage-directory }} +branding: + color: purple + icon: filter \ No newline at end of file diff --git a/golang/entrypoint.sh b/golang/entrypoint.sh new file mode 100644 index 0000000..a8f2dd2 --- /dev/null +++ b/golang/entrypoint.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -e + +# Check if the givet root directory exists +if ! [ -d "$1" ] ; then + echo "Root project dir not found." + echo "USAGE: "$0" " + exit 1 +fi + +pathToCommonDirectory="./common" +. $pathToCommonDirectory"/commonWrapper.sh" + +# Check that package.json is provided +if ! [ -e "$1/go.mod" ] ; then + echo "go.mod not found in $1" + exit 1 +fi + +module_dir="${1%/}" +module_dir="/github/workspace/$module_dir" + +# install dependencies +cd $module_dir && go install + +exitIfNotInstalled python3 + +exitIfNotInstalled go + +outputFileName=".debricked-call-graph-golang" + +# Run the actual script that generates the call graph +echo "Running call graph generator" +/vulnfunc/golang/src/gen_callgraph.sh "$module_dir" $outputFileName + +formatOutput "/vulnfunc/golang/src/$outputFileName" \ No newline at end of file diff --git a/golang/src/combine_ast_ssa.py b/golang/src/combine_ast_ssa.py new file mode 100644 index 0000000..951cb21 --- /dev/null +++ b/golang/src/combine_ast_ssa.py @@ -0,0 +1,82 @@ +import json +import getopt +import sys +import os + +def combine_ast_ssa(ast_symbols, ssa_dump, output_file): + """ combine_ast_ssa takes two lists of the same symbols (ast_symbols + and ssa_dump), combines the information and saves it in the given + output_file. + + Paramaters: + ast_symbols (list[dict]): A list of all the symbols from the ast_parser. + ssa_dump (list[dict]): A list of all the symbols from the ssa build. + output_file (string): The file where the combined symbols list will + be saved. + + Returns: + void + """ + # add easy access to the symbols in ssa_dump through the 'Location' + ssa_by_location = {} + for symbol in ssa_dump: + if 'Location' in symbol and 'Synthetic' not in symbol: + ssa_by_location[symbol['Location']] = symbol + + # For each symbol in ast_symbols add a footprint given by the + # ssa_dump 'Name' + if ast_symbols != None: + for i in range(len(ast_symbols)): + Location = ast_symbols[i]['file'] + ":" + ast_symbols[i]['line_start'] + ":" + ast_symbols[i]['column_start'] + if Location not in ssa_by_location: + if ast_symbols[i]['file'][-8:] != '_test.go': + # print a warning if a symbol was found in the AST-parser + # bit not in the ssa build. + print("Warning! " + Location + " not found when building") + else: + ast_symbols[i]['footprint'] = ssa_by_location[Location]['Name'] + else: + ast_symbols = [] + + with open(output_file, "w") as f: + f.write(json.dumps(ast_symbols, indent=4, sort_keys=True)) + + +def main(argv): + """ main takes command line arguments and calls combine_ast_ssa + accordingly. + + Paramaters: + argv (list[string]) all the given command line arguments + + Returns: + void + """ + try: + opts, args = getopt.getopt(argv, "ho:", ["parsed_ssadump=", "ast_symbols="]) + except getopt.GetoptError: + print("combine_ast_ssa.py --parsed_ssadump= --ast_symbols= -o ") + sys.exit(2) + + # set default values + ast_symbols = {} + ssa_dump = {} + output_file = "symbols.json" + + # parse the flags + for opt, arg in opts: + if opt == '--parsed_ssadump': + with open(arg, "r") as f: + ssa_dump = json.load(f) + elif opt == '--ast_symbols': + with open(arg, "r") as f: + ast_symbols = json.load(f) + elif opt == '-o': + output_file = os.path.abspath(arg) + elif opt == '-h': + print("Usage: combine_ast_ssa.py --parsed_ssadump --ast_symbols -o ") + + combine_ast_ssa(ast_symbols, ssa_dump, output_file) + +if __name__ == "__main__": + main(sys.argv[1:]) \ No newline at end of file diff --git a/golang/src/convert_cg.py b/golang/src/convert_cg.py new file mode 100644 index 0000000..6d3948d --- /dev/null +++ b/golang/src/convert_cg.py @@ -0,0 +1,123 @@ +import sys +import getopt +import os +import json +from pathlib import Path +import subprocess + +def conv_cg(output_file): + """ conv_cg reads a call graph from standard in on the format + {{.Caller}} file:{{.Filename}} {{.Line}} {{.Column}}--->{{.Callee}} from the command + callgraph from https://pkg.go.dev/golang.org/x/tools, converts + into the following format: https://github.com/debricked/vulnerable-functionality/wiki/Output-format + and saves it as a json. + + Example of a call graph inputted through standard in: + debricked.com/go-test-module/hello.Main file:/home/teodor/debricked/vulnerable-functionality/golang/test/hello/hello.go 34 28--->github.com/google/go-github/v36/github.NewClient + debricked.com/go-test-module/hello.Main file:/home/teodor/debricked/vulnerable-functionality/golang/test/hello/hello.go 36 46--->(*github.com/google/go-github/v36/github.RepositoriesService).ListByOrg + debricked.com/go-test-module/hello.Main file:/home/teodor/debricked/vulnerable-functionality/golang/test/hello/hello.go 36 65--->context.Background + debricked.com/go-test-module/hello.Main file:/home/teodor/debricked/vulnerable-functionality/golang/test/hello/hello.go 38 12--->fmt.Print + debricked.com/go-test-module/hello.Main file:/home/teodor/debricked/vulnerable-functionality/golang/test/hello/hello.go 38 33--->(*github.com/google/go-github/v36/github.Repository).GetFullName + + Parameters: + output_file (string): the file were the output json-file is saved. + + Returns: + void + """ + + # these are the packages we will parse later to get better symbol information + packages_to_parse = set() + + # the adjacency list with all edges reversed. + cg = {} + for inp_line in sys.stdin: + # split at the "special" charachter "--->" + line = inp_line.split("--->") + source_call = line[0].split() + source = source_call[0] + call_package_folder = str(Path(source_call[1][5:]).parent) + packages_to_parse.add(call_package_folder) + target = line[1][:-1] + if target not in cg: + cg[target] = [] + if (source, (int(source_call[2]), int(source_call[3]))) not in cg[target]: + cg[target].append((source, (int(source_call[2]), int(source_call[3])))) + + # parse the saved packages to get information about the symbols such as position + # note that we currently only parse the packages where calls are made from + # since it is rather difficult to obtain the paths to the functions being called + + data_symbols = {} + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # loop thorugh the packages that the calls come from and parse them so that we get information about the symbols + for package_to_parse in packages_to_parse: + cmd = [os.path.join(script_dir, "run.sh"), os.path.join(package_to_parse)] + completed_process = subprocess.run(cmd) + + # if we can parse the package, load the symbols + if not completed_process.returncode: + with open(os.path.join(script_dir, "symbols.json"), "r") as f: + part_symbols = json.load(f) + + for symbol in part_symbols: + if 'footprint' in symbol: + data_symbols[symbol['footprint']] = symbol + + # convert the reversed adjacancy list into the correct output format by adding the information + # from the parsed symbols + list_cg = {} + list_cg['version'] = 2 + list_cg['data'] = [] + for footprint in cg: + # if footprint isn't in data_symbols it means that we didn't find when parsing + if footprint in data_symbols: + symbol = data_symbols[footprint] + # TODO: Implement a check for isApplicationClass and isStandardLibraryClass, i.e. the second and third argument in new_element + new_element = [footprint, False, \ + False, "-", str(Path(symbol['file']).name), symbol['line_start'], symbol['line_end']] + else: + new_element = [footprint, False, \ + False, "-", "unknown", "unknown", "unknown"] + + # calless is all the functions calling footprint + callees = [] + for callee in cg[footprint]: + callees.append([callee[0], callee[1][0]]) + new_element.append(callees) + list_cg['data'].append(new_element) + + + with open(output_file, "w") as f: + f.write(json.dumps(list_cg, indent=4, sort_keys=True)) + + +def main(argv): + """ main parses the command line arguments (only permit the flag -o) + and calls the function that converts the call graph into the + proper format and saves it. + + Paramaters: + argv (list[string]): the given command line arguments + + Returns: + void + """ + # set default output_file + output_file = "cg.json" + # parse command line arguments + try: + opts, args = getopt.getopt(argv, "o:") + except getopt.GetoptError: + print("Incorrect usage!") + sys.exit(2) + for opt, arg in opts: + if opt == '-o': + output_file = os.path.abspath(arg) + + # do what the script is for + conv_cg(output_file) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/golang/src/gen_callgraph.sh b/golang/src/gen_callgraph.sh new file mode 100755 index 0000000..f5efa5e --- /dev/null +++ b/golang/src/gen_callgraph.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# usage: ./gen_callgraph.sh /input/main/package outputFileName + +rm_if_exist() { + if [ -e "./$1" ] ; then + rm $1 + fi +} + +# get the script dir to find all files easily +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +# partial_file1 is used for storing the edges from the pointer analysis +partial_file1="$SCRIPT_DIR/.partial_cg1.out" +# output_file is where the call graph finally is stored +output_file="$SCRIPT_DIR/$2" + +echo "Generating edges" +# change dir to the package directory and run the callgraph command +(cd $1 && callgraph -format='{{.Caller}} file:{{.Filename}} {{.Line}} {{.Column}}--->{{.Callee}}' -algo=pta .) \ +| sort | uniq > $partial_file1 +echo "Done generating edges" + +# convert the graph to a nice looking json adjacency list +echo "Grouping the calls to construct a function -> function call graph" +python3 $SCRIPT_DIR/convert_cg.py -o $output_file < $partial_file1 +echo "Call graph generation done" +echo $output_file +# clean up +rm_if_exist $partial_file1 \ No newline at end of file diff --git a/golang/src/go.mod b/golang/src/go.mod new file mode 100644 index 0000000..4b1e130 --- /dev/null +++ b/golang/src/go.mod @@ -0,0 +1,5 @@ +module debricked.com/go_parser + +go 1.16 + +require golang.org/x/tools v0.1.4 // indirect diff --git a/golang/src/go.sum b/golang/src/go.sum new file mode 100644 index 0000000..8e8328c --- /dev/null +++ b/golang/src/go.sum @@ -0,0 +1,24 @@ +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/golang/src/go_parser b/golang/src/go_parser new file mode 100755 index 0000000..237b050 Binary files /dev/null and b/golang/src/go_parser differ diff --git a/golang/src/parse.go b/golang/src/parse.go new file mode 100644 index 0000000..abc8c50 --- /dev/null +++ b/golang/src/parse.go @@ -0,0 +1,74 @@ +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "go/ast" + "go/parser" + "go/token" + "log" + "os" + "strconv" +) + +// ast_parse_files parses all the files passed through files. +// The files are parsed using "go/parser" and then inspected using "go/ast.Inspect". +// All the ast.Node objects in the AST that have type *ast.Funcl and *ast.FuncLit are filtered out. +// These symbols are converted to json-format and printed to stdout. +func ast_parse_files(files []string) { + + // create a FileSet for all the files, this is used for tracking positions. + fset := token.NewFileSet() + // all symbols will be in the symbols variable + var symbols []map[string]string + // loop through all the given files and parse them + for _, file_path := range files { + file, err := parser.ParseFile(fset, file_path, nil, 0) + if err != nil { + log.Fatal(err) + } + // inspect (traverse) the AST, note that the 'footprint' is added at a later stage + ast.Inspect(file, func(n ast.Node) bool { + // Find Function declarations + fn_dec, ok_dec := n.(*ast.FuncDecl) + if ok_dec { + + symbol := map[string]string{"file": file_path, "line_start": strconv.Itoa(fset.Position(fn_dec.Name.Pos()).Line), + "line_end": strconv.Itoa(fset.Position(fn_dec.End()).Line), "column_start": strconv.Itoa(fset.Position(fn_dec.Name.Pos()).Column), + "type": "Function Declaration", "language": "go"} + + symbols = append(symbols, symbol) + + return true + } + // Find Function literals + fn_lit, ok_lit := n.(*ast.FuncLit) + if ok_lit { + + symbol := map[string]string{"file": file_path, "line_start": strconv.Itoa(fset.Position(fn_lit.Pos()).Line), + "line_end": strconv.Itoa(fset.Position(fn_lit.End()).Line), "column_start": strconv.Itoa(fset.Position(fn_lit.Pos()).Column), + "type": "Function literal", "language": "go"} + + symbols = append(symbols, symbol) + return true + } + return true + }) + } + // print the output to stdout + output, _ := json.Marshal(symbols) + fmt.Print(string(output)) + +} + +func main() { + // read all files to be parsed from stdin + scanner := bufio.NewScanner(os.Stdin) + var files []string + for scanner.Scan() { + files = append(files, scanner.Text()) + } + // call ast_parse_files for the files read from stdin + ast_parse_files(files) +} diff --git a/golang/src/parse_ssadump.py b/golang/src/parse_ssadump.py new file mode 100644 index 0000000..d61ef1e --- /dev/null +++ b/golang/src/parse_ssadump.py @@ -0,0 +1,67 @@ +import sys +import getopt +import os +import json + +def parse_ssadump(output_file): + """ parse_ssadump reads the output from ssadump from + https://pkg.go.dev/golang.org/x/tools though standard in, parses the + symbols and saves them in json format in the specified output file. + + Paramaters: + output_file (string): The path to the output file. + + Returns: + void + """ + symbols = [] + in_symbol = False + # run ssadump to see an example of how the output looks. + for line in sys.stdin: + if in_symbol and len(line) > 0 and line[0] == "#": + try: + format_line = line[2:-1].split() + symbols[-1][format_line[0][:-1]] = "".join(format_line[1]) + except IndexError: + continue + elif in_symbol and len(line) > 0 and line[0] != "#": + in_symbol = False + elif not in_symbol and len(line) > 0 and line[0] == "#": + in_symbol = True + symbols.append({}) + try: + format_line = line[2:-1].split() + symbols[-1][format_line[0][:-1]] = "".join(format_line[1]) + except IndexError: + continue + + with open(output_file, "w") as f: + f.write(json.dumps(symbols, indent=4, sort_keys=True)) + + +def main(argv): + """ main parses the command line arguments (only permit the flag -o) + and calls the function that converts output from ssadump into + a symbol list. + + Paramaters: + argv (list[string]): the given command line arguments + + Returns: + void + """ + # set default output_file + output_file = "parsed_ssadump.json" + input_package = "" + try: + opts, args = getopt.getopt(argv, "o:") + except getopt.GetoptError: + print("Incorrect usage!") + sys.exit(2) + for opt, arg in opts: + if opt == '-o': + output_file = os.path.abspath(arg) + parse_ssadump(output_file) + +if __name__ == "__main__": + main(sys.argv[1:]) \ No newline at end of file diff --git a/golang/src/run.sh b/golang/src/run.sh new file mode 100755 index 0000000..83229e6 --- /dev/null +++ b/golang/src/run.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Usage: ./run.sh /path/to/package/directory + +# utility function used for cleaning up partial files. +rm_if_exist() { + if [ -e "$1" ] ; then + rm $1 + fi +} + +# get the script dir to find all files easily +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +# ssadump and parse the output +(cd $1 && ssadump -build=F .) | python3 $SCRIPT_DIR/parse_ssadump.py -o "$SCRIPT_DIR/.parsed_ssadump.json" + +# ast_parse +all_files=$SCRIPT_DIR/".all_go_files.in" +rm_if_exist $all_files + +go_files=($1/*.go) +for go_file in "${go_files[@]}"; do + abs_path="$(realpath $go_file)" + echo $abs_path >> $all_files +done + +# combine the ast_symbols and ssa_dump and save the final symbols list in "symbol.json" +$SCRIPT_DIR/go_parser < $all_files > "$SCRIPT_DIR/.ast_symbols.json" +python3 $SCRIPT_DIR/combine_ast_ssa.py --parsed_ssadump="$SCRIPT_DIR/.parsed_ssadump.json" --ast_symbols="$SCRIPT_DIR/.ast_symbols.json" -o "$SCRIPT_DIR/symbols.json" + +# clean up +# rm_if_exist "$SCRIPT_DIR/.parsed_ssadump.json" +# rm_if_exist "$SCRIPT_DIR/.ast_symbols.json" +# rm_if_exist $all_files + diff --git a/golang/test/go.mod b/golang/test/go.mod new file mode 100644 index 0000000..c2a30ea --- /dev/null +++ b/golang/test/go.mod @@ -0,0 +1,9 @@ +module debricked.com/go-test-module + +go 1.16 + +require ( + github.com/artdarek/go-unzip v1.0.0 + github.com/google/go-github/v36 v36.0.0 + golang.org/x/example v0.0.0-20210407023211-09c3a5e06b5d +) diff --git a/golang/test/go.sum b/golang/test/go.sum new file mode 100644 index 0000000..5e4fd05 --- /dev/null +++ b/golang/test/go.sum @@ -0,0 +1,37 @@ +github.com/artdarek/go-unzip v1.0.0 h1:Ja9wfhiXyl67z5JT37rWjTSb62KXDP+9jHRkdSREUvg= +github.com/artdarek/go-unzip v1.0.0/go.mod h1:KhX4LV7e4UwWCTo7orBYnJ6LJ/dZTI6jXxUg69hO/C8= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github/v36 v36.0.0 h1:ndCzM616/oijwufI7nBRa+5eZHLldT+4yIB68ib5ogs= +github.com/google/go-github/v36 v36.0.0/go.mod h1:LFlKC047IOqiglRGNqNb9s/iAPTnnjtlshm+bxp+kwk= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/example v0.0.0-20210407023211-09c3a5e06b5d h1:Jb7QkRA2r4uVZbCS1hKaVd6DLQUQ79pmpAOaFeOfRGM= +golang.org/x/example v0.0.0-20210407023211-09c3a5e06b5d/go.mod h1:+yakPl5KR9J+ysfUNADYwEU5qeqjUO473wDktD4xMYw= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20210112183307-1e6ecd4bf1b0/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/golang/test/go_cg_test.py b/golang/test/go_cg_test.py new file mode 100644 index 0000000..b7a7a35 --- /dev/null +++ b/golang/test/go_cg_test.py @@ -0,0 +1,54 @@ +import subprocess +import json +import os +from pathlib import Path + +def test_call_graph(): + path_to_src = Path(os.path.realpath(__file__)).parent.parent / "src" + path_to_src = path_to_src.resolve() + + path_to_test = Path(os.path.realpath(__file__)).parent + + cmd = [str(path_to_src / "gen_callgraph.sh"), str(path_to_test), "cg.json"] + subprocess.run(cmd) + + with open(str(path_to_src / "cg.json"), "r") as f: + cg = json.load(f) + + data = cg['data'] + interesting_called_funcs = {} + for func in data: + if func[0] in ["github.com/google/go-github/v36/github.NewClient", \ + "debricked.com/go-test-module/hello.adder$1", \ + "debricked.com/go-test-module/hello.Main", \ + "(*debricked.com/go-test-module/hello.Fruit).GetName", \ + "github.com/google/go-github/v36/github.sanitizeURL", \ + "debricked.com/go-test-module/hello.Main$1"]: + interesting_called_funcs[func[0]] = func + + # call to external library + assert 'github.com/google/go-github/v36/github.NewClient' in interesting_called_funcs + assert 'debricked.com/go-test-module/hello.Main' in [x[0] for x in interesting_called_funcs['github.com/google/go-github/v36/github.NewClient'][-1]] + + # call to closure function + assert "debricked.com/go-test-module/hello.adder$1" in interesting_called_funcs + assert 'debricked.com/go-test-module/hello.Main' in [x[0] for x in interesting_called_funcs["debricked.com/go-test-module/hello.adder$1"][-1]] + + # regular function call in different package + assert "debricked.com/go-test-module/hello.Main" in interesting_called_funcs + assert 'debricked.com/go-test-module.main' in [x[0] for x in interesting_called_funcs["debricked.com/go-test-module/hello.Main"][-1]] + + # call to method + assert "(*debricked.com/go-test-module/hello.Fruit).GetName" in interesting_called_funcs + assert 'debricked.com/go-test-module/hello.Main' in [x[0] for x in interesting_called_funcs["(*debricked.com/go-test-module/hello.Fruit).GetName"][-1]] + + # call in external library + assert "github.com/google/go-github/v36/github.sanitizeURL" in interesting_called_funcs + assert '(*github.com/google/go-github/v36/github.AbuseRateLimitError).Error' in [x[0] for x in interesting_called_funcs["github.com/google/go-github/v36/github.sanitizeURL"][-1]] + + # internal call to user written code + assert "debricked.com/go-test-module/hello.Main$1" in interesting_called_funcs + assert 'sort.doPivot_func' in [x[0] for x in interesting_called_funcs["debricked.com/go-test-module/hello.Main$1"][-1]] + + # clean up + subprocess.run(["rm", str(path_to_src / "cg.json")]) \ No newline at end of file diff --git a/golang/test/hello/hello.go b/golang/test/hello/hello.go new file mode 100644 index 0000000..ff9bea2 --- /dev/null +++ b/golang/test/hello/hello.go @@ -0,0 +1,60 @@ +package hello + +import ( + "context" + "fmt" + "sort" + + "github.com/google/go-github/v36/github" + "golang.org/x/example/stringutil" +) + +type Fruit struct { + name string +} + +func (fruit *Fruit) GetName() string { + return fruit.name +} + +func init() { + fmt.Print("Init hej\n") +} + +func adder() func(int) int { + sum := 0 + return func(x int) int { + sum += x + return sum + } +} + +func Main() { + + client := github.NewClient(nil) + opt := &github.RepositoryListByOrgOptions{Type: "public"} + repos, _, _ := client.Repositories.ListByOrg(context.Background(), "debricked", opt) + for i := 0; i < len(repos); i++ { + fmt.Print(repos[i].GetFullName()) + fmt.Print("\n\n") + } + fmt.Print("Hejsan\n") + fmt.Print(stringutil.Reverse("\nnasjeH")) + + frukt := Fruit{"Äpple"} + fmt.Println(frukt.GetName()) + + people := []string{"Alice", "Bob", "Dave"} + sort.Slice(people, func(i, j int) bool { + return len(people[i]) < len(people[j]) + }) + fmt.Println(people) + + pos, neg := adder(), adder() + for i := 0; i < 10; i++ { + fmt.Println( + pos(i), + neg(-2*i), + ) + } +} diff --git a/golang/test/hello/util.go b/golang/test/hello/util.go new file mode 100644 index 0000000..a61d0f5 --- /dev/null +++ b/golang/test/hello/util.go @@ -0,0 +1,7 @@ +package hello + +import "fmt" + +func PrintPhrase() { + fmt.Println("Hello from util.go in package hello") +} diff --git a/golang/test/main.go b/golang/test/main.go new file mode 100644 index 0000000..e89d5f0 --- /dev/null +++ b/golang/test/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + + "debricked.com/go-test-module/hello" +) + +func main() { + hello.Main() + // Below, this CVE is demonstrated: https://github.com/golang/vulndb/blob/master/reports/GO-2020-0041.yaml + //tz.PackTo("/home/teodor/debricked/go_test/helloworld", "/home/teodor/debricked/go_test/helloworld_tar") + //tz.ExtractTo("/home/teodor/debricked/go_test/helloworld_tar", "/home/teodor/debricked/go_test/extracted") + fmt.Println("Main function has runned") +} diff --git a/java/gradle/gradleWrapper.sh b/java/gradle/gradleWrapper.sh index 883986b..228319d 100755 --- a/java/gradle/gradleWrapper.sh +++ b/java/gradle/gradleWrapper.sh @@ -6,7 +6,7 @@ if ! [ -d "$1" ] ; then exit 1 fi -$pathToCommonDirectory = "/vulnfunc/java/common" +$pathToCommonDirectory = "/vulnfunc/common" . $pathToCommonDirectory"/commonWrapper.sh" projectRootDirectory="${1%/}" @@ -35,7 +35,7 @@ gradle -q compileJava cd $cwd pathToSootWrapper=$pathToCommonDirectory"/SootWrapper-0.1-jar-with-dependencies.jar" -outputFileName=".debricked-call-graph" +outputFileName=".debricked-call-graph-java" echo "Running SootWrapper" java -jar $pathToSootWrapper -u $projectRootDirectory"/build/classes/java/main" -l $dependencyDir -f $outputFileName diff --git a/java/maven/mavenWrapper.sh b/java/maven/mavenWrapper.sh index 3b61626..497036e 100755 --- a/java/maven/mavenWrapper.sh +++ b/java/maven/mavenWrapper.sh @@ -6,7 +6,7 @@ if ! [ -d "$1" ] ; then exit 1 fi -pathToCommonDirectory="/vulnfunc/java/common" +pathToCommonDirectory="/vulnfunc/common" . $pathToCommonDirectory"/commonWrapper.sh" projectRootDirectory="${1%/}" @@ -22,7 +22,7 @@ echo "Compiling "$projectRootDirectory mvn -q -B -f $projectRootDirectory compile --fail-at-end pathToSootWrapper=$pathToCommonDirectory"/SootWrapper-0.1-jar-with-dependencies.jar" -outputFileName=".debricked-call-graph" +outputFileName=".debricked-call-graph-java" echo "Running SootWrapper" java -jar $pathToSootWrapper -u $projectRootDirectory"/target/classes" -l $dependencyDir -f $outputFileName diff --git a/javascript/Dockerfile b/javascript/Dockerfile new file mode 100644 index 0000000..0e5b95e --- /dev/null +++ b/javascript/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1 + +FROM nikolaik/python-nodejs:python3.8-nodejs14-slim +RUN npm install -g @persper/js-callgraph + +COPY ./src /vulnfunc/javascript/src +COPY ./test /vulnfunc/javascript/test +RUN chmod +x /vulnfunc/javascript/src/entrypoint.sh + +RUN apt-get update ; apt-get install --no-install-recommends -y zip +RUN pip install -r /vulnfunc/javascript/src/requirements.txt + +ENTRYPOINT [ "/vulnfunc/javascript/src/entrypoint.sh", "/vulnfunc/javascript/test/fresh_install"] \ No newline at end of file diff --git a/javascript/action.yml b/javascript/action.yml new file mode 100644 index 0000000..c6b236f --- /dev/null +++ b/javascript/action.yml @@ -0,0 +1,17 @@ +name: Debricked Vulnerable Functionality for JavaScript +author: Debricked +description: Calculates the call graph for a JavaScript project +inputs: + root-packagejson-directory: + description: Directory containing the root package.json and package-lock.json of the project to analyse + required: true + +runs: + using: docker + image: docker://debricked/vulnerable-functionality:javascript + entrypoint: /vulnfunc/javascript/src/entrypoint.sh + args: + - ${{ inputs.root-packagejson-directory }} +branding: + color: purple + icon: filter \ No newline at end of file diff --git a/javascript/src/entrypoint.sh b/javascript/src/entrypoint.sh new file mode 100755 index 0000000..5484610 --- /dev/null +++ b/javascript/src/entrypoint.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -e + +# Check if the givet root directory exists +if ! [ -d "$1" ] ; then + echo "Root project dir not found." + echo "USAGE: "$0" " + exit 1 +fi + +pathToCommonDirectory="/vulnfunc/common" +. $pathToCommonDirectory"/commonWrapper.sh" + +# Check that package.json is provided +if ! [ -e "$1/package.json" ] ; then + echo "package.json not found in $1" + exit 1 +fi + +package_dir="${1%/}" + +# Check if package-lock.json is provided, this is preferable +# since we then run npm ci instead of npm instal. +if [ -e "$1/package-lock.json" ] ; then + npm --prefix $package_dir ci +elif [ -e "$1/yarn.lock"] ; then + yarn --cwd $package_dir install +else + echo "Warning, neither yarn.lock nor package-lock.json was found, proceeding with just package.json" + npm --prefix $package_dir install +fi + +exitIfNotInstalled python3 + +outputFileName=".debricked-call-graph-javascript" + +# Run the actual python script that generates the call graph +echo "Running gen_package_cg" +python3 /vulnfunc/javascript/src/gen_package_cg.py -i $package_dir -o $outputFileName + +formatOutput $outputFileName \ No newline at end of file diff --git a/javascript/src/gen_package_cg.py b/javascript/src/gen_package_cg.py new file mode 100644 index 0000000..e7eb8a2 --- /dev/null +++ b/javascript/src/gen_package_cg.py @@ -0,0 +1,674 @@ +import glob +import os +import json +import esprima +import re +import subprocess +import sys +import getopt +from intervaltree import IntervalTree +from pathlib import Path +import warnings +import time +import multiprocessing as mp +import pickle + +# for some reason we get recursion limit without this on some small packages +sys.setrecursionlimit(10**6) + +def remove_unreachable(starting_nodes, cg): + """ remove_unreachable takes a call graph and starting nodes and filters + out the nodes that aren't reachable from the starting nodes + + Paramater: + starting_nodes (list[string]): the starting nodes in the traversal + cg (dict): The call graph to be "cleaned" + + Returns: + dict, the cleaned call graph + """ + + starting_nodes = [x for x in starting_nodes if x in cg] + clean_cg = {} + vis = set() + vis.union(starting_nodes) + que = starting_nodes + ind = 0 + + while ind < len(que): + curr = que[ind] + if curr in cg: + clean_cg[curr] = cg[curr] + for neigh in cg[curr]: + if neigh not in vis: + vis.add(neigh) + que.append(neigh) + ind += 1 + return clean_cg + + +def find_source_files(package_folder): + """ find_source_files takes a root package folder and returns a list of + all .js files not in the dircetories node_modules or test, also all + paths containing "test" after package_folder are filtered out + + Paramaters: + package_folder (string): Path to the root package directory + + Returns: + list[string]: All the found source files ending wih .js + """ + # ignore folders test and node_modules + SOURCE_IGNORE = ["test", "node_modules"] + package_dir = os.listdir(package_folder) + + # add the .js files directly in the main directory + source_code_in_package = glob.glob(package_folder + "*.js") + + # loop through all subdirectories and add all .js files there recursively + # for subdir in filter(lambda x: os.path.isdir(package_folder + x) and x not in SOURCE_IGNORE, package_dir): + for subdir in [x for x in package_dir if os.path.isdir(package_folder + x) and x not in SOURCE_IGNORE]: + js_glob_path = os.path.join(package_folder, subdir, "**/*.js") + source_code_in_package.extend(glob.glob(js_glob_path, recursive=True)) + + # filter out all paths that contain "test" after the package folder + source_code_in_package = [x for x in source_code_in_package if "test" not in x[len(package_folder):] and Path(x).is_file()] + return source_code_in_package + +def slice_data(data, nprocs): + """ slice_data splits a list in nprocs almost equally long lists. + + Paramaters: + data (list[]): The list to be partitioned. + nprocs (int): The number of lists data should be partitioned to. + + Returns: + list[list[]]: A list with all the partitions. + """ + slices = [[] for i in range(nprocs)] + for i in range(len(data)): + slices[i % nprocs].append(data[i]) + return slices + +def gen_cg_for_package(package_folder, output_file, cg_memory): + start = time.time() + """" gen_cg_for_package calculates the call graph for the package in + package_folder and its dependencies and saves the call graph to + output_file. The call graph is represented as an adjacency list of + functions and is saved in json format. + + Paramaters: + package_folder (string): Path to the root package directory containing + package.json and package-lock.json. + + output_file (string): Path to the output file where the call graph + is stored. + + cg_memory (int): The target number of bytes to be used in each call + to the third party js-callgraph. + + Returns: + void + """ + # the rest of the program requires the path to end with / so add it if there isn't any + if package_folder[-1] != "/": + package_folder += "/" + + global cg_time; cg_time = 0 + global parse_time; parse_time = 0 + global esprima_time; esprima_time = 0 + + + cg = {} + cg_calls = [] + global symbol_ranges_to_footprint; symbol_ranges_to_footprint = {} + global interval_trees; interval_trees = {} + global incorrect_syntax_files; incorrect_syntax_files = [] + global dep_graph; dep_graph = {} + global files_in_packages; files_in_packages = [] + global files_to_dep; files_to_dep = {} + global package_to_files; package_to_files = {} + global time_per_package; time_per_package = {} + global file_size; file_size = {} + global space_time; space_time = [] + data_symbols = {} + visited_packages = set() + + # recursive function that will be called for each package in the dep-tree + def rec_gen_cg(rec_package_folder): + # print("Currently calculating call graph for", rec_package_folder, "and its dependencies.") + + # make sure that the current package is not visited again + visited_packages.add(rec_package_folder) + + #create an empty set in the dependency graph + dep_graph[rec_package_folder] = set() + + # load dependencies + try: + with open(rec_package_folder + 'package.json') as f: + package_conf = json.load(f) + dependencies = list(package_conf["dependencies"].keys()) + except (FileNotFoundError, KeyError) as e: + # package.json is not necesary, but in this case we assume the package has no dependencies + dependencies = [] + + # add the source files of the current package to various global data containers + source_code_in_curr_package = find_source_files(rec_package_folder) + files_in_packages.extend(source_code_in_curr_package) + for script in source_code_in_curr_package: + files_to_dep[script] = rec_package_folder + if rec_package_folder not in package_to_files: + package_to_files[rec_package_folder] = [] + package_to_files[rec_package_folder].append(script) + + + # Loop through all dependencies and explore them recursively + + for dep in dependencies: + + # find the dependency + dep_found = False + + # last_checked_for_modules represents where we last looked for node_modules + last_checked_for_modules = Path(rec_package_folder) + max_iterations = 1000 + iterations = 0 + while not dep_found: + # look for node_modules on the different levels + curr_folder = last_checked_for_modules + while not (curr_folder / "node_modules").exists() and curr_folder != Path("/"): + + # this moves the string one step up in the dir tree + curr_folder = curr_folder.parent + + # assert that we have found a node_modules directory + assert (curr_folder / "node_modules").exists(), "node_modules with " + dep + " not found for " + rec_package_folder + + # set the next folder to check for the dependency, this is needed if the found + # node_modules does not contain our search after package + last_checked_for_modules = curr_folder.parent + + node_modules = curr_folder / "node_modules" + + # check if the dependency is a single file (very rare but can happen) + if (node_modules / dep / ".js").exists() and (node_modules / dep / ".js").is_file(): + dep_found = True + files_in_packages.append(str(node_modules / (dep + ".js"))) + files_to_dep[str(node_modules / (dep + ".js"))] = str(node_modules / (dep + ".js")) + package_to_files[str(node_modules / (dep + ".js"))] = [str(node_modules / (dep + ".js"))] + + # check if the imported package is a folder + elif (node_modules / dep).exists(): + dep_found = True + dep_path = str(curr_folder / "node_modules" / dep) + "/" + # add the dependency to the dep graph + dep_graph[rec_package_folder].add(dep_path) + + # continue the recursion if the dependency is not already visited + if dep_path not in visited_packages: + visited_packages.add(dep_path) + rec_gen_cg(dep_path) + + iterations += 1 + # break when we have looked in to many node_modules directories. + assert iterations < max_iterations, "Dependency not found after " + max_iterations + " searches." + + # recursively explore the dependency graph and find the files + + print("Exploring the dependency graphs and finding source files") + rec_gen_cg(package_folder) + + print("Begin parsing") + + # parse all files in parallell + + nprocs = mp.cpu_count() + pool = mp.Pool(processes=nprocs) + + parse_time_start = time.time() + + if os.path.isfile("saved_parse.p") and False: + parsed = pickle.load( open("saved_parse.p", "rb") ) + interval_trees = parsed['interval_trees'] + symbol_ranges_to_footprint = parsed['symbol_ranges_to_footprint'] + incorrect_syntax_files = parsed['incorrect_syntax_files'] + file_size = parsed['file_size'] + else: + + slices = slice_data(files_in_packages, nprocs) + multi_result = [pool.apply_async(parse_files, (inp, )) for inp in slices] + for p in multi_result: + (p_int_tree, p_range_to_symbol, p_incorrect_syntax, p_file_size, p_data_symbols) = p.get() + interval_trees.update(p_int_tree) + symbol_ranges_to_footprint.update(p_range_to_symbol) + incorrect_syntax_files.extend(p_incorrect_syntax) + file_size.update(p_file_size) + data_symbols.update(p_data_symbols) + + parsed = {} + parsed['interval_trees'] = interval_trees + parsed['symbol_ranges_to_footprint'] = symbol_ranges_to_footprint + parsed['incorrect_syntax_files'] = incorrect_syntax_files + parsed['file_size'] = file_size + pickle.dump( parsed, open("saved_parse.p", "wb") ) + + print("Parsing done, beginning to generate call graph") + + parse_time_end = time.time() + parse_time = parse_time_end - parse_time_start + + # generate the call graph + + pool.close() + pool.join() + + pool = mp.Pool(processes=nprocs) + + cg_package_vis = set() + + # cg_memory is the targeted size in bytes for all the files in each js-callgraph run + cg_memory = 2500000 + + # calculate internal cg edges for large projects + + print("Calculating all internal edges") + + for package in package_to_files: + total_package_memory = 0 + for file in package_to_files[package]: + if file in file_size: + total_package_memory += file_size[file] + if total_package_memory >= cg_memory // 2: + cg_calls.append(package_to_files[package]) + + print("Memory for", package, "is", total_package_memory) + + print("Done calculating internal edges") + + def package_visitor(curr_main_package): + + print("Currently generating call graph for", curr_main_package, "and its dependencies") + + cg_package_vis.add(curr_main_package) + deps = list(dep_graph[curr_main_package]) + + # dep_files consists of all the files in the direct dependencies + + dep_files = [] + for dep in deps: + if dep in package_to_files: + # dep will not be in package_to_files if the package is empty of .js-files + dep_files.extend(package_to_files[dep]) + + dep_files = [x for x in dep_files if x not in incorrect_syntax_files] + + # check if curr_main_package contains any files + if curr_main_package in package_to_files: + curr_package_files = package_to_files[curr_main_package] + else: + curr_package_files = [] + + curr_package_files = [x for x in curr_package_files if x not in incorrect_syntax_files] + + # we can split the current main package curr_memory tracks how much memory we fill + # for the gen_cg_for_files - call. The strategu consists of filling half that + # memory with the files from the curr_main_package and half of it with ofther files. + # This could probably be optimized by analysing the enire dep and main package size. + + curr_memory = 0 + last_path_i = 0 + source_memory = 0 + for path_i, path in enumerate(curr_package_files): + curr_memory += file_size[path] + source_memory += file_size[path] + if curr_memory >= cg_memory // 2 or path_i == len(curr_package_files)-1: + last_dep_i = 0 + dep_memory = 0 + if len(dep_files) == 0: + cg_calls.append(curr_package_files[last_path_i:path_i+1]) + for dep_i, dep_path in enumerate(dep_files): + curr_memory += file_size[dep_path] + dep_memory += file_size[dep_path] + if curr_memory >= cg_memory or dep_i == len(dep_files)-1: + cg_calls.append(curr_package_files[last_path_i:path_i+1] + dep_files[last_dep_i:dep_i+1]) + last_dep_i = dep_i+1 + curr_memory -= dep_memory + dep_memory = 0 + last_path_i = path_i+1 + curr_memory -= source_memory + curr_memory = 0 + + for dep in deps: + if dep not in cg_package_vis: + package_visitor(dep) + + # call package_visitor that recursively exploroes the dependency graphs and + # adds the calls to gen_cg_for_files in the list cg_calls + package_visitor(package_folder) + + # run the calls in parallell + multi_result = [pool.apply_async(gen_cg_for_files, (inp, )) for inp in cg_calls] + for p_res in multi_result: + p_cg = p_res.get() + if p_cg != None: + # add the results from single calls to cg + for key in p_cg: + if key in cg: + cg[key] = list(set(cg[key]).union(set(p_cg[key]))) + else: + cg[key] = p_cg[key] + + pool.close() + pool.join() + + # the filtering below is currently commented out because of the new call graph format makes it + # harder to filter away the unecessary nodes. + + # filter away all the nodes that aren't reachable from the files in package_folder + # start_nodes = [] + # for source_file in package_to_files[package_folder]: + # for func in interval_trees[source_file]: + # start_nodes.append(func.data['footprint']) + + # cg = remove_unreachable(start_nodes, cg) + + + # format call graph to new format, cg is curently a dict with footprint -> a list of (footprint, (start_row, start_col)) + # where start_row and start_col are where the call is made. Below it is formatted to the following format: + # https://github.com/debricked/vulnerable-functionality/wiki/Output-format + + list_cg = {} + list_cg['version'] = 2 + list_cg['data'] = [] + for footprint in cg: + symbol = data_symbols[footprint] + new_element = [footprint, files_to_dep[symbol['file']] == package_folder, \ + False, "-", symbol['file_name'], symbol['row_start'], symbol['row_end']] + callees = [] + for callee in cg[footprint]: + callees.append([callee[0], callee[1][0]]) + new_element.append(callees) + list_cg['data'].append(new_element) + + with open(output_file, "w") as f: + f.write(json.dumps(list_cg, indent=4, sort_keys=True)) + + end = time.time() + + for partial_cg in glob.glob("partial_cg*.json"): + os.remove(partial_cg) + + print("Total time:", end - start) + print("Parse time:", parse_time, parse_time / (end - start) * 100, "% of the total time") + + +def parse_files(source_files): + + """ parse_files takes a list of files and parses them. The parsing consists of + finding all functions and saving them in an Interval Tree and the dict + symbol_ranges_to_footprint. It also saves all unparsable files in + incorrect_syntax_files and adds an artificial "global" ast node. + + Paramaters: + source_files (list[string]): A list with all the paths to the files to be parsed. + + Returns: + (dict, dict, list[string], dict) + + string -> IntervalTree dict where an IntervalTree is stored + for each file. + + string -> (int, int) -> string dict that saves + the symbol footprints for each file and range (i.e. the column if the entire + file was written on a single row). + + a list with all the files that caused parsing errors. + + string -> int dict that saves the number of bytes each file occupates. + """ + + interval_trees = {} + symbol_ranges_to_footprint = {} + incorrect_syntax_files = [] + file_size = {} + data_symbols = {} + + program_path = "" + + # filter_nodes is called on for every node in the AST. + def filter_nodes(node, metadata): + # check if it is a relevant symbol, if so add it so symbols. + if node.type in ["FunctionExpression", "FunctionDeclaration", "ArrowFunctionExpression"]: + # check if it has a name + if hasattr(node, "id") and node.id != None: + name = node.id.name + else: + name = "anonymous" + + footprint = program_path + "/" + name + "_at_" + str(node.loc.start.line) + ":" + str(node.loc.start.column) + + symbol = {"footprint" : footprint, "range" : node.range, "file": file_path} + + data_symbol = {"footprint" : footprint, "range" : node.range, "file_name": Path(file_path).name, \ + "row_start": node.loc.start.line, "row_end": node.loc.end.line, "file": file_path} + + # check if we have visited this file before, if not create a new IntervalTree + if symbol['file'] not in interval_trees: + interval_trees[symbol['file']] = IntervalTree() + symbol_ranges_to_footprint[symbol['file']] = {} + # add the function to the tree, we use symbol['range'][1]+1 in since the intervals are + # non inclusive at the right limit, but inclusive for the left limit. So when we query + # the interval tree it is as though we have inclusion at both ends. + interval_trees[symbol['file']].addi(symbol['range'][0], symbol['range'][1]+1, symbol) + symbol_ranges_to_footprint[symbol['file']][tuple(symbol['range'])] = symbol['footprint'] + data_symbols[data_symbol['footprint']] = data_symbol + + # loop through all the files to be parsed and parse them + for prog_nbr, prog in enumerate(source_files): + + p = Path(prog) + p = p.resolve() + file_path = str(p) + program_path = str(p) + + package = Path(files_to_dep[prog]) + try: + with open(str(package / "package.json"), "r") as f: + package_json = json.load(f) + name = package_json['name'] + except (FileNotFoundError, KeyError) as e: + # if not missing_packagejson_warning_done: + warnings.warn("package-json not found or name not found in package.json for " + str(p) + " using folder name instead") + # missing_packagejson_warning_done = True + name = package.name + + # remove the first part of the path + program_path = program_path[len(str(package)):] + # add the package name instead + program_path = name + program_path + + # make sure that the files are in unix format + cmd = ["dos2unix", "-q", prog] + subprocess.run(cmd, stdout=subprocess.DEVNULL) + + with open(prog, encoding='utf-8') as f: + program = f.read() + + # handle hashbang/shebang by replacing the first line with blank spaces if it starts with #! + program = re.sub('^#!(.*)', lambda x: len(x.group(0))*" ", program) + + # use esprima to parse the module. When each node is created filter_nodes is called. + # filter_nodes checks if the node is a function and then stores it in the intervaltree. + # and in symbol_ranges_to_footprint + try: + esprima.parseModule(program, {"loc":True, "range": True}, filter_nodes) + except esprima.error_handler.Error: + warnings.warn("Parser is not able to parse " + prog + ", will ignore it from now on") + incorrect_syntax_files.append(prog) + continue + + # add artificial node for the entire script named global + + symbol = {"file": file_path, "footprint" : program_path + "/global", "range" : (0, len(program))} + + # data_symbol is a bigger symbol containing all the function data + data_symbol = {"file_name": Path(file_path).name, "footprint" : program_path + "/global", "range" : (0, len(program)), \ + "row_start": 0, "row_end": len(prog.split("\n"))+1, "file": file_path} + + # check if we have visited this file before, if not create a new IntervalTree + try: + if symbol['file'] not in interval_trees: + interval_trees[symbol['file']] = IntervalTree() + symbol_ranges_to_footprint[symbol['file']] = {} + # add the function to the tree + interval_trees[symbol['file']].addi(symbol['range'][0], symbol['range'][1]+1, symbol) + symbol_ranges_to_footprint[symbol['file']][tuple(symbol['range'])] = symbol['footprint'] + # add the function to the data_dict + data_symbols[data_symbol['footprint']] = data_symbol + except ValueError: + warnings.warn("Unable to add global node to " + symbol['footprint'] + " will ignore it from now on") + incorrect_syntax_files.append(prog) + continue + + p = Path(prog) + p = p.resolve() + file_size[prog] = p.stat().st_size + + return (interval_trees, symbol_ranges_to_footprint, incorrect_syntax_files, file_size, data_symbols) + +def gen_cg_for_files(all_cg_files): + """ gen_cg_for_files calls js-callgraph with a subprocess and returns + the edges. + + Paramaters: + all_cg_files (list[string]): All the paths to the source code + + Returns: + cg (dict): A string -> list[string] dict that saved the edges in an + adjecency list form. + """ + cg = {} + global symbol_ranges_to_footprint + global cg_time + global files_to_dep + global interval_trees + global incorrect_syntax_files + global file_size + + def symbol_containing_call(source_call): + # returns the symbol that contains the source_call, this is done by taking + # all functions containing the call and then selecting the smallest one since + # this one must be the one directly containing the call + + # pick out all functions containing the call by intersecting all intervals + # containing the the start and end of the call and pick the smallest interval + functions_over_call = list(map(lambda x: x.data, \ + interval_trees[source_call['file']][source_call['range']['start']] & \ + interval_trees[source_call['file']][source_call['range']['end']])) + + min_covering_function = min(functions_over_call, key = lambda x: x['range'][1] - x['range'][0]) + + return min_covering_function['footprint'] + + def target_search(target_call): + # returns the exact function corresponding to the specific target function + + if target_call['file'] == 'Native': + return 'Native' + try: + return symbol_ranges_to_footprint[target_call['file']][(target_call['range']['start'], target_call['range']['end'])] + except KeyError: + warnings.warn("Javascript and python has probably parsed " + target_call['file'] + " differently, ignoring the calls to this file") + return 'Native' + + + all_cg_files = [x for x in all_cg_files if x not in incorrect_syntax_files] + + # print the total memory used in the js-callgraph call + tot_memory = 0 + for file in all_cg_files: + tot_memory += file_size[file] + print("Total memory for the cg run:", tot_memory / 1000, "kB") + + # if all files are incorrect we should return immediately, elsewise we will read the old + # partial_cg.json + if len(all_cg_files) == 0: + return + + # Call js-callgraph which is an open source static call graph generations tool + # implementing the approximate call graph algorithm. + cmd = ["js-callgraph", "--cg"] + cmd.extend(all_cg_files) + cmd.extend(["--output", "partial_cg_" + str(os.getpid()) + ".json"]) + + print("Starting node cg process...") + + print("Number of files:", len(all_cg_files)) + + completed_process = subprocess.run(cmd, stdout=subprocess.DEVNULL) + + print("Done with node cg process...") + + if not completed_process.returncode: + with open("partial_cg_" + str(os.getpid()) + ".json", "r") as f: + partial_cg = json.load(f) + else: + # return None if the call graph generation failed + return None + + # loop over all the edges found and add them to cg. + for call in partial_cg: + # check if the call is in the wrong direction, if so, skip it + try: + if call['target']['file'] != 'Native': + target_in_dep = files_to_dep[call['target']['file']] in dep_graph[files_to_dep[call['source']['file']]] + same_package = files_to_dep[call['target']['file']] == files_to_dep[call['source']['file']] + if not target_in_dep and not same_package: + continue + source_symbol = symbol_containing_call(call['source']) + target_symbol = target_search(call['target']) + + if target_symbol != "Native" and target_symbol not in cg: + cg[target_symbol] = [] + if target_symbol != "Native" and (source_symbol, (call['source']['start']['row'], call['source']['start']['column'])) not in cg[target_symbol]: + cg[target_symbol].append((source_symbol, (call['source']['start']['row'], call['source']['start']['column']))) + except ValueError: + warnings.warn("Was not able to find enclosing function for \n" + str(call) + "\n ignoring this call. The underlaying reason is probably that python and javascript have parsed it differently.") + + return cg + + + +def main(argv): + """ main takes command line arguments and runs gen_cg_for_package + accordingly. + + Paramaters: + argv (list[string]) all command line arguments supplied + + Returns: + void + """ + # set default values + output_file = "cg.json" + input_package = "" + cg_memory = 2500000 + try: + opts, args = getopt.getopt(argv, "hi:o:m:") + except getopt.GetoptError: + print("gen_package_cg.py -i -o -m ") + sys.exit(2) + for opt, arg in opts: + if opt == '-i': + input_package = os.path.abspath(arg) + elif opt == '-o': + output_file = os.path.abspath(arg) + elif opt == '-m': + cg_memory = int(arg) + elif opt == '-h': + print("Usage: gen_package_cg.py -i -o -m ") + gen_cg_for_package(input_package, output_file, cg_memory) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/javascript/src/package.json b/javascript/src/package.json new file mode 100644 index 0000000..c100926 --- /dev/null +++ b/javascript/src/package.json @@ -0,0 +1,16 @@ +{ + "name": "vulnfunc_javascript", + "version": "1.0.0", + "description": "Extracts the call graph for a JavaScript project.", + "scripts": { + "test": "pytest" + }, + "keywords": [ + "javascript" + ], + "author": "Debricked", + "license": "GPL-3.0-only", + "dependencies": { + "@persper/js-callgraph": "1.3.2" + } + } \ No newline at end of file diff --git a/javascript/src/requirements.txt b/javascript/src/requirements.txt new file mode 100644 index 0000000..9df557b --- /dev/null +++ b/javascript/src/requirements.txt @@ -0,0 +1,2 @@ +esprima==4.0.1 +intervaltree==3.1.0 \ No newline at end of file diff --git a/javascript/test/basics_arrow/arrow.js b/javascript/test/basics_arrow/arrow.js new file mode 100644 index 0000000..11f4dce --- /dev/null +++ b/javascript/test/basics_arrow/arrow.js @@ -0,0 +1,3 @@ +arr_func = x => { return x; }; + +arr_func(10); diff --git a/javascript/test/basics_arrow/package.json b/javascript/test/basics_arrow/package.json new file mode 100644 index 0000000..fbe9abb --- /dev/null +++ b/javascript/test/basics_arrow/package.json @@ -0,0 +1,11 @@ +{ + "name": "basics_arrow", + "version": "1.0.0", + "description": "", + "main": "arrow.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/javascript/test/basics_arrow/test_answer.truth b/javascript/test/basics_arrow/test_answer.truth new file mode 100644 index 0000000..e02508b --- /dev/null +++ b/javascript/test/basics_arrow/test_answer.truth @@ -0,0 +1,20 @@ +{ + "data": [ + [ + "basics_arrow/arrow.js/anonymous_at_1:11", + true, + false, + "-", + "arrow.js", + 1, + 1, + [ + [ + "basics_arrow/arrow.js/global", + 3 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/basics_assignment/assignment.js b/javascript/test/basics_assignment/assignment.js new file mode 100644 index 0000000..0c08661 --- /dev/null +++ b/javascript/test/basics_assignment/assignment.js @@ -0,0 +1,11 @@ +// This file tests assigning a function object to an existing local variable +function func() { + let c = 1; + let b = function new_func () { + console.log('new_func is called!'); + }; + c = b; + c(); +} + +func(); diff --git a/javascript/test/basics_assignment/package.json b/javascript/test/basics_assignment/package.json new file mode 100644 index 0000000..645a11f --- /dev/null +++ b/javascript/test/basics_assignment/package.json @@ -0,0 +1,11 @@ +{ + "name": "basics_assignment", + "version": "1.0.0", + "description": "", + "main": "arrow.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/javascript/test/basics_assignment/test_answer.truth b/javascript/test/basics_assignment/test_answer.truth new file mode 100644 index 0000000..71786c9 --- /dev/null +++ b/javascript/test/basics_assignment/test_answer.truth @@ -0,0 +1,35 @@ +{ + "data": [ + [ + "basics_assignment/assignment.js/func_at_2:0", + true, + false, + "-", + "assignment.js", + 2, + 9, + [ + [ + "basics_assignment/assignment.js/global", + 11 + ] + ] + ], + [ + "basics_assignment/assignment.js/new_func_at_4:9", + true, + false, + "-", + "assignment.js", + 4, + 6, + [ + [ + "basics_assignment/assignment.js/func_at_2:0", + 8 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/basics_global-as-prop/global-as-prop.js b/javascript/test/basics_global-as-prop/global-as-prop.js new file mode 100644 index 0000000..e64f587 --- /dev/null +++ b/javascript/test/basics_global-as-prop/global-as-prop.js @@ -0,0 +1,5 @@ +let func_1 = function (x) { return x; }; +let func_2 = { 'func_1': function (x) { return x; }}; + +func_2.func_1() +func_1() diff --git a/javascript/test/basics_global-as-prop/package.json b/javascript/test/basics_global-as-prop/package.json new file mode 100644 index 0000000..ec184d5 --- /dev/null +++ b/javascript/test/basics_global-as-prop/package.json @@ -0,0 +1,11 @@ +{ + "name": "basics_global-as-prop", + "version": "1.0.0", + "description": "", + "main": "arrow.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/javascript/test/basics_global-as-prop/test_answer.truth b/javascript/test/basics_global-as-prop/test_answer.truth new file mode 100644 index 0000000..3c32099 --- /dev/null +++ b/javascript/test/basics_global-as-prop/test_answer.truth @@ -0,0 +1,35 @@ +{ + "data": [ + [ + "basics_global-as-prop/global-as-prop.js/anonymous_at_1:13", + true, + false, + "-", + "global-as-prop.js", + 1, + 1, + [ + [ + "basics_global-as-prop/global-as-prop.js/global", + 5 + ] + ] + ], + [ + "basics_global-as-prop/global-as-prop.js/anonymous_at_2:25", + true, + false, + "-", + "global-as-prop.js", + 2, + 2, + [ + [ + "basics_global-as-prop/global-as-prop.js/global", + 4 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/basics_local_is_fine/local-is-fine.js b/javascript/test/basics_local_is_fine/local-is-fine.js new file mode 100644 index 0000000..f96b8f0 --- /dev/null +++ b/javascript/test/basics_local_is_fine/local-is-fine.js @@ -0,0 +1,6 @@ +(function () { + let func_1 = function (x) { return x; }; + let func_2 = { 'func_1': function (x) { return x; }}; + func_2.func_1() + func_1() +}()); diff --git a/javascript/test/basics_local_is_fine/package.json b/javascript/test/basics_local_is_fine/package.json new file mode 100644 index 0000000..7f09a28 --- /dev/null +++ b/javascript/test/basics_local_is_fine/package.json @@ -0,0 +1,11 @@ +{ + "name": "basics_local_is_fine", + "version": "1.0.0", + "description": "", + "main": "local-is-fine.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/javascript/test/basics_local_is_fine/test_answer.truth b/javascript/test/basics_local_is_fine/test_answer.truth new file mode 100644 index 0000000..b244704 --- /dev/null +++ b/javascript/test/basics_local_is_fine/test_answer.truth @@ -0,0 +1,50 @@ +{ + "data": [ + [ + "basics_local_is_fine/local-is-fine.js/anonymous_at_1:1", + true, + false, + "-", + "local-is-fine.js", + 1, + 6, + [ + [ + "basics_local_is_fine/local-is-fine.js/global", + 1 + ] + ] + ], + [ + "basics_local_is_fine/local-is-fine.js/anonymous_at_2:15", + true, + false, + "-", + "local-is-fine.js", + 2, + 2, + [ + [ + "basics_local_is_fine/local-is-fine.js/anonymous_at_1:1", + 5 + ] + ] + ], + [ + "basics_local_is_fine/local-is-fine.js/anonymous_at_3:27", + true, + false, + "-", + "local-is-fine.js", + 3, + 3, + [ + [ + "basics_local_is_fine/local-is-fine.js/anonymous_at_1:1", + 4 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/basics_method_def/method-def.js b/javascript/test/basics_method_def/method-def.js new file mode 100644 index 0000000..da3ecb9 --- /dev/null +++ b/javascript/test/basics_method_def/method-def.js @@ -0,0 +1,11 @@ +var test_obj = { + some_method1: function () { + return 37; + }, + 'some_method2': function () { + return 'blue'; + } +} + +test_obj.some_method1(); +test_obj.some_method2(); diff --git a/javascript/test/basics_method_def/package.json b/javascript/test/basics_method_def/package.json new file mode 100644 index 0000000..5b15868 --- /dev/null +++ b/javascript/test/basics_method_def/package.json @@ -0,0 +1,11 @@ +{ + "name": "basics_method_def", + "version": "1.0.0", + "description": "", + "main": "method-def.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/javascript/test/basics_method_def/test_answer.truth b/javascript/test/basics_method_def/test_answer.truth new file mode 100644 index 0000000..4c2a918 --- /dev/null +++ b/javascript/test/basics_method_def/test_answer.truth @@ -0,0 +1,35 @@ +{ + "data": [ + [ + "basics_method_def/method-def.js/anonymous_at_2:18", + true, + false, + "-", + "method-def.js", + 2, + 4, + [ + [ + "basics_method_def/method-def.js/global", + 10 + ] + ] + ], + [ + "basics_method_def/method-def.js/anonymous_at_5:20", + true, + false, + "-", + "method-def.js", + 5, + 7, + [ + [ + "basics_method_def/method-def.js/global", + 11 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/es6_module/lib.js b/javascript/test/es6_module/lib.js new file mode 100644 index 0000000..0cf16b9 --- /dev/null +++ b/javascript/test/es6_module/lib.js @@ -0,0 +1,7 @@ +export const sqrt = Math.sqrt; +export function square(x) { + return x * x; +} +export function diag(a, b) { + return sqrt(square(a) + square(b)); +} diff --git a/javascript/test/es6_module/main.js b/javascript/test/es6_module/main.js new file mode 100644 index 0000000..b4a4de5 --- /dev/null +++ b/javascript/test/es6_module/main.js @@ -0,0 +1,3 @@ +import { square, diag } from 'lib'; +console.log(square(12)); // 144 +console.log(diag(12, 5)); // 13 diff --git a/javascript/test/es6_module/package.json b/javascript/test/es6_module/package.json new file mode 100644 index 0000000..9063682 --- /dev/null +++ b/javascript/test/es6_module/package.json @@ -0,0 +1,12 @@ +{ + "name": "es6_module", + "version": "1.0.0", + "description": "", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "type": "module" +} diff --git a/javascript/test/es6_module/test_answer.truth b/javascript/test/es6_module/test_answer.truth new file mode 100644 index 0000000..abc9b39 --- /dev/null +++ b/javascript/test/es6_module/test_answer.truth @@ -0,0 +1,43 @@ +{ + "data": [ + [ + "es6_module/lib.js/square_at_2:7", + true, + false, + "-", + "lib.js", + 2, + 4, + [ + [ + "es6_module/lib.js/diag_at_5:7", + 6 + ], + [ + "es6_module/lib.js/diag_at_5:7", + 6 + ], + [ + "es6_module/main.js/global", + 2 + ] + ] + ], + [ + "es6_module/lib.js/diag_at_5:7", + true, + false, + "-", + "lib.js", + 5, + 7, + [ + [ + "es6_module/main.js/global", + 3 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/fresh_install/index.js b/javascript/test/fresh_install/index.js new file mode 100644 index 0000000..4c6f25b --- /dev/null +++ b/javascript/test/fresh_install/index.js @@ -0,0 +1,3 @@ +const leftPad = require('left-pad'); + +console.log(leftPad(17, 5, 0)) diff --git a/javascript/test/fresh_install/package-lock.json b/javascript/test/fresh_install/package-lock.json new file mode 100644 index 0000000..fa2df0d --- /dev/null +++ b/javascript/test/fresh_install/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "fresh_install", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "left-pad": "^1.3.0" + } + }, + "node_modules/left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "deprecated": "use String.prototype.padStart()" + } + }, + "dependencies": { + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" + } + } +} diff --git a/javascript/test/fresh_install/package.json b/javascript/test/fresh_install/package.json new file mode 100644 index 0000000..ea6d0ae --- /dev/null +++ b/javascript/test/fresh_install/package.json @@ -0,0 +1,14 @@ +{ + "name": "fresh_install", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "left-pad": "^1.3.0" + } +} diff --git a/javascript/test/function_in_paranthesis/package.json b/javascript/test/function_in_paranthesis/package.json new file mode 100644 index 0000000..00b1e33 --- /dev/null +++ b/javascript/test/function_in_paranthesis/package.json @@ -0,0 +1,12 @@ +{ + "name": "function_in_paranthesis", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/function_in_paranthesis/script.js b/javascript/test/function_in_paranthesis/script.js new file mode 100644 index 0000000..6f2efde --- /dev/null +++ b/javascript/test/function_in_paranthesis/script.js @@ -0,0 +1,8 @@ + +(function (){ + function fun2(){ + console.log('Ḧello World'); + } + fun2(); +}()); + diff --git a/javascript/test/ignore_specific_keyword/library.js b/javascript/test/ignore_specific_keyword/library.js new file mode 100644 index 0000000..ae9e266 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/ignore_specific_keyword/node_modules/lib1/index.js b/javascript/test/ignore_specific_keyword/node_modules/lib1/index.js new file mode 100644 index 0000000..09d71d2 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/node_modules/lib1/index.js @@ -0,0 +1,7 @@ +let lib1_fun = function (){ + console.log("This function is called from lib1"); +} + +module.exports = { + lib1_fun +} diff --git a/javascript/test/ignore_specific_keyword/node_modules/lib1/package.json b/javascript/test/ignore_specific_keyword/node_modules/lib1/package.json new file mode 100644 index 0000000..f9ffe92 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/node_modules/lib1/package.json @@ -0,0 +1,12 @@ +{ + "name": "lib1", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/ignore_specific_keyword/package.json b/javascript/test/ignore_specific_keyword/package.json new file mode 100644 index 0000000..8755ba8 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/package.json @@ -0,0 +1,14 @@ +{ + "name": "ignore_specific_keyword", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib1": "1.0.0" + } +} diff --git a/javascript/test/ignore_specific_keyword/script.js b/javascript/test/ignore_specific_keyword/script.js new file mode 100644 index 0000000..21fbf83 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/script.js @@ -0,0 +1,22 @@ +lib = require('./library'); +lib1 = require('lib1'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; +let are = lib.area; + +// Calling the functions +// defined in the lib module +function fun (){ + are(length, breadth); + lib.perimeter(length, breadth); + lib1.lib1_fun(); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); diff --git a/javascript/test/ignore_specific_keyword/src/javascript/common/common.js b/javascript/test/ignore_specific_keyword/src/javascript/common/common.js new file mode 100644 index 0000000..b01900c --- /dev/null +++ b/javascript/test/ignore_specific_keyword/src/javascript/common/common.js @@ -0,0 +1,5 @@ +function common() { + console.log("This is declared in common folder"); +} + +common(); diff --git a/javascript/test/ignore_specific_keyword/src/javascript/js_script.js b/javascript/test/ignore_specific_keyword/src/javascript/js_script.js new file mode 100644 index 0000000..f5886c2 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/src/javascript/js_script.js @@ -0,0 +1,9 @@ +function b() { + console.log("called from root/src/javascript"); +} + +b(); + +module.exports = { + b +} \ No newline at end of file diff --git a/javascript/test/ignore_specific_keyword/src/javascript/test_js_script.js b/javascript/test/ignore_specific_keyword/src/javascript/test_js_script.js new file mode 100644 index 0000000..f882ad2 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/src/javascript/test_js_script.js @@ -0,0 +1,6 @@ +main = require('./js_script') +function test() { + main.b(); +} + +test() \ No newline at end of file diff --git a/javascript/test/ignore_specific_keyword/src/javascript/test_node_modules/js_script.js b/javascript/test/ignore_specific_keyword/src/javascript/test_node_modules/js_script.js new file mode 100644 index 0000000..7df02b8 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/src/javascript/test_node_modules/js_script.js @@ -0,0 +1,9 @@ +function c() { + console.log("Declared in test_node_modules"); +} + +c(); + +module.exports = { + c +} \ No newline at end of file diff --git a/javascript/test/ignore_specific_keyword/src/javascript/test_node_modules/test_js_script.js b/javascript/test/ignore_specific_keyword/src/javascript/test_node_modules/test_js_script.js new file mode 100644 index 0000000..e8cb425 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/src/javascript/test_node_modules/test_js_script.js @@ -0,0 +1,6 @@ +main = require('./js_script') +function test() { + main.c(); +} + +test() diff --git a/javascript/test/ignore_specific_keyword/src/src_script.js b/javascript/test/ignore_specific_keyword/src/src_script.js new file mode 100644 index 0000000..f6969e0 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/src/src_script.js @@ -0,0 +1,9 @@ +function a() { + console.log("called from root/src"); +} + +a(); + +module.exports = { + a +} \ No newline at end of file diff --git a/javascript/test/ignore_specific_keyword/src/test_src_script.js b/javascript/test/ignore_specific_keyword/src/test_src_script.js new file mode 100644 index 0000000..51e7718 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/src/test_src_script.js @@ -0,0 +1,6 @@ +main = require('./src_script') +function test() { + main.a(); +} + +test() \ No newline at end of file diff --git a/javascript/test/ignore_specific_keyword/test_answer.truth b/javascript/test/ignore_specific_keyword/test_answer.truth new file mode 100644 index 0000000..f8ada96 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/test_answer.truth @@ -0,0 +1,110 @@ +{ + "data": [ + [ + "ignore_specific_keyword/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "ignore_specific_keyword/script.js/fun_at_14:0", + 15 + ] + ] + ], + [ + "ignore_specific_keyword/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "ignore_specific_keyword/script.js/fun_at_14:0", + 16 + ] + ] + ], + [ + "ignore_specific_keyword/script.js/fun_at_14:0", + true, + false, + "-", + "script.js", + 14, + 18, + [ + [ + "ignore_specific_keyword/script.js/global", + 20 + ] + ] + ], + [ + "ignore_specific_keyword/src/src_script.js/a_at_1:0", + true, + false, + "-", + "src_script.js", + 1, + 3, + [ + [ + "ignore_specific_keyword/src/src_script.js/global", + 5 + ] + ] + ], + [ + "ignore_specific_keyword/src/javascript/js_script.js/b_at_1:0", + true, + false, + "-", + "js_script.js", + 1, + 3, + [ + [ + "ignore_specific_keyword/src/javascript/js_script.js/global", + 5 + ] + ] + ], + [ + "ignore_specific_keyword/src/javascript/common/common.js/common_at_1:0", + true, + false, + "-", + "common.js", + 1, + 3, + [ + [ + "ignore_specific_keyword/src/javascript/common/common.js/global", + 5 + ] + ] + ], + [ + "lib1/index.js/anonymous_at_1:15", + false, + false, + "-", + "index.js", + 1, + 3, + [ + [ + "ignore_specific_keyword/script.js/fun_at_14:0", + 17 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/ignore_specific_keyword/test_something.js b/javascript/test/ignore_specific_keyword/test_something.js new file mode 100644 index 0000000..41b94c7 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/test_something.js @@ -0,0 +1,7 @@ + +function fun (){ + console.log("This is a test!"); +} + +fun(); + diff --git a/javascript/test/ignore_specific_keyword/tests/script.js b/javascript/test/ignore_specific_keyword/tests/script.js new file mode 100644 index 0000000..41b94c7 --- /dev/null +++ b/javascript/test/ignore_specific_keyword/tests/script.js @@ -0,0 +1,7 @@ + +function fun (){ + console.log("This is a test!"); +} + +fun(); + diff --git a/javascript/test/import_from_inside/library.js b/javascript/test/import_from_inside/library.js new file mode 100644 index 0000000..ebdf2c1 --- /dev/null +++ b/javascript/test/import_from_inside/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' units!'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/import_from_inside/node_modules/lib1/index.js b/javascript/test/import_from_inside/node_modules/lib1/index.js new file mode 100644 index 0000000..09d71d2 --- /dev/null +++ b/javascript/test/import_from_inside/node_modules/lib1/index.js @@ -0,0 +1,7 @@ +let lib1_fun = function (){ + console.log("This function is called from lib1"); +} + +module.exports = { + lib1_fun +} diff --git a/javascript/test/import_from_inside/node_modules/lib1/package.json b/javascript/test/import_from_inside/node_modules/lib1/package.json new file mode 100644 index 0000000..f9ffe92 --- /dev/null +++ b/javascript/test/import_from_inside/node_modules/lib1/package.json @@ -0,0 +1,12 @@ +{ + "name": "lib1", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/import_from_inside/package.json b/javascript/test/import_from_inside/package.json new file mode 100644 index 0000000..e6a5f11 --- /dev/null +++ b/javascript/test/import_from_inside/package.json @@ -0,0 +1,14 @@ +{ + "name": "import_from_inside", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib1": "1.0.0" + } +} diff --git a/javascript/test/import_from_inside/script.js b/javascript/test/import_from_inside/script.js new file mode 100644 index 0000000..21fbf83 --- /dev/null +++ b/javascript/test/import_from_inside/script.js @@ -0,0 +1,22 @@ +lib = require('./library'); +lib1 = require('lib1'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; +let are = lib.area; + +// Calling the functions +// defined in the lib module +function fun (){ + are(length, breadth); + lib.perimeter(length, breadth); + lib1.lib1_fun(); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); diff --git a/javascript/test/import_from_inside/test_answer.truth b/javascript/test/import_from_inside/test_answer.truth new file mode 100644 index 0000000..5f5cf7c --- /dev/null +++ b/javascript/test/import_from_inside/test_answer.truth @@ -0,0 +1,65 @@ +{ + "data": [ + [ + "import_from_inside/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "import_from_inside/script.js/fun_at_14:0", + 15 + ] + ] + ], + [ + "import_from_inside/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "import_from_inside/script.js/fun_at_14:0", + 16 + ] + ] + ], + [ + "import_from_inside/script.js/fun_at_14:0", + true, + false, + "-", + "script.js", + 14, + 18, + [ + [ + "import_from_inside/script.js/global", + 20 + ] + ] + ], + [ + "lib1/index.js/anonymous_at_1:15", + false, + false, + "-", + "index.js", + 1, + 3, + [ + [ + "import_from_inside/script.js/fun_at_14:0", + 17 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/import_from_outside/library.js b/javascript/test/import_from_outside/library.js new file mode 100644 index 0000000..ae9e266 --- /dev/null +++ b/javascript/test/import_from_outside/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/import_from_outside/package.json b/javascript/test/import_from_outside/package.json new file mode 100644 index 0000000..1622941 --- /dev/null +++ b/javascript/test/import_from_outside/package.json @@ -0,0 +1,14 @@ +{ + "name": "import_from_outside", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib2": "1.0.0" + } +} diff --git a/javascript/test/import_from_outside/script.js b/javascript/test/import_from_outside/script.js new file mode 100644 index 0000000..a5b21f4 --- /dev/null +++ b/javascript/test/import_from_outside/script.js @@ -0,0 +1,22 @@ +lib = require('./library'); +lib2 = require('lib2'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; +let are = lib.area; + +// Calling the functions +// defined in the lib module +function fun (){ + are(length, breadth); + lib.perimeter(length, breadth); + lib2.lib2_fun(); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); diff --git a/javascript/test/import_from_outside/test_answer.truth b/javascript/test/import_from_outside/test_answer.truth new file mode 100644 index 0000000..5701ce8 --- /dev/null +++ b/javascript/test/import_from_outside/test_answer.truth @@ -0,0 +1,65 @@ +{ + "data": [ + [ + "import_from_outside/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "import_from_outside/script.js/fun_at_14:0", + 15 + ] + ] + ], + [ + "import_from_outside/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "import_from_outside/script.js/fun_at_14:0", + 16 + ] + ] + ], + [ + "import_from_outside/script.js/fun_at_14:0", + true, + false, + "-", + "script.js", + 14, + 18, + [ + [ + "import_from_outside/script.js/global", + 20 + ] + ] + ], + [ + "lib2/index.js/anonymous_at_1:15", + false, + false, + "-", + "index.js", + 1, + 3, + [ + [ + "import_from_outside/script.js/fun_at_14:0", + 17 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/import_priority/library.js b/javascript/test/import_priority/library.js new file mode 100644 index 0000000..ae9e266 --- /dev/null +++ b/javascript/test/import_priority/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/import_priority/node_modules/lib1/index.js b/javascript/test/import_priority/node_modules/lib1/index.js new file mode 100644 index 0000000..ce91d6a --- /dev/null +++ b/javascript/test/import_priority/node_modules/lib1/index.js @@ -0,0 +1,7 @@ +let lib1_fun = function (){ + console.log("This function is called from lib1 inside node_modules"); +} + +module.exports = { + lib1_fun +} diff --git a/javascript/test/import_priority/node_modules/lib1/package.json b/javascript/test/import_priority/node_modules/lib1/package.json new file mode 100644 index 0000000..f9ffe92 --- /dev/null +++ b/javascript/test/import_priority/node_modules/lib1/package.json @@ -0,0 +1,12 @@ +{ + "name": "lib1", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/import_priority/package.json b/javascript/test/import_priority/package.json new file mode 100644 index 0000000..090a6eb --- /dev/null +++ b/javascript/test/import_priority/package.json @@ -0,0 +1,14 @@ +{ + "name": "import_priority", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib1": "1.0.0" + } +} diff --git a/javascript/test/import_priority/script.js b/javascript/test/import_priority/script.js new file mode 100644 index 0000000..21fbf83 --- /dev/null +++ b/javascript/test/import_priority/script.js @@ -0,0 +1,22 @@ +lib = require('./library'); +lib1 = require('lib1'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; +let are = lib.area; + +// Calling the functions +// defined in the lib module +function fun (){ + are(length, breadth); + lib.perimeter(length, breadth); + lib1.lib1_fun(); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); diff --git a/javascript/test/import_priority/test_answer.truth b/javascript/test/import_priority/test_answer.truth new file mode 100644 index 0000000..d385c05 --- /dev/null +++ b/javascript/test/import_priority/test_answer.truth @@ -0,0 +1,65 @@ +{ + "data": [ + [ + "import_priority/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "import_priority/script.js/fun_at_14:0", + 15 + ] + ] + ], + [ + "import_priority/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "import_priority/script.js/fun_at_14:0", + 16 + ] + ] + ], + [ + "import_priority/script.js/fun_at_14:0", + true, + false, + "-", + "script.js", + 14, + 18, + [ + [ + "import_priority/script.js/global", + 20 + ] + ] + ], + [ + "lib1/index.js/anonymous_at_1:15", + false, + false, + "-", + "index.js", + 1, + 3, + [ + [ + "import_priority/script.js/fun_at_14:0", + 17 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/incorrect_dep/library.js b/javascript/test/incorrect_dep/library.js new file mode 100644 index 0000000..9292e3c --- /dev/null +++ b/javascript/test/incorrect_dep/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} diff --git a/javascript/test/incorrect_dep/node_modules/lib1/index.js b/javascript/test/incorrect_dep/node_modules/lib1/index.js new file mode 100644 index 0000000..d906a34 --- /dev/null +++ b/javascript/test/incorrect_dep/node_modules/lib1/index.js @@ -0,0 +1,9 @@ +let lib1_fun = function (){ + // {; is an incorrect symbol so the program cant be parsed + {; + console.log("This function is called from lib1"); +} + +module.exports = { + lib1_fun +} diff --git a/javascript/test/incorrect_dep/node_modules/lib1/package.json b/javascript/test/incorrect_dep/node_modules/lib1/package.json new file mode 100644 index 0000000..f9ffe92 --- /dev/null +++ b/javascript/test/incorrect_dep/node_modules/lib1/package.json @@ -0,0 +1,12 @@ +{ + "name": "lib1", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/incorrect_dep/package.json b/javascript/test/incorrect_dep/package.json new file mode 100644 index 0000000..ca5a518 --- /dev/null +++ b/javascript/test/incorrect_dep/package.json @@ -0,0 +1,14 @@ +{ + "name": "incorrect_dep", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib1": "1.0.0" + } +} diff --git a/javascript/test/incorrect_dep/script.js b/javascript/test/incorrect_dep/script.js new file mode 100644 index 0000000..21fbf83 --- /dev/null +++ b/javascript/test/incorrect_dep/script.js @@ -0,0 +1,22 @@ +lib = require('./library'); +lib1 = require('lib1'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; +let are = lib.area; + +// Calling the functions +// defined in the lib module +function fun (){ + are(length, breadth); + lib.perimeter(length, breadth); + lib1.lib1_fun(); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); diff --git a/javascript/test/incorrect_dep/test_answer.truth b/javascript/test/incorrect_dep/test_answer.truth new file mode 100644 index 0000000..241dbcb --- /dev/null +++ b/javascript/test/incorrect_dep/test_answer.truth @@ -0,0 +1,50 @@ +{ + "data": [ + [ + "incorrect_dep/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "incorrect_dep/script.js/fun_at_14:0", + 15 + ] + ] + ], + [ + "incorrect_dep/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "incorrect_dep/script.js/fun_at_14:0", + 16 + ] + ] + ], + [ + "incorrect_dep/script.js/fun_at_14:0", + true, + false, + "-", + "script.js", + 14, + 18, + [ + [ + "incorrect_dep/script.js/global", + 20 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/node_modules/lib1/index.js b/javascript/test/node_modules/lib1/index.js new file mode 100644 index 0000000..09d71d2 --- /dev/null +++ b/javascript/test/node_modules/lib1/index.js @@ -0,0 +1,7 @@ +let lib1_fun = function (){ + console.log("This function is called from lib1"); +} + +module.exports = { + lib1_fun +} diff --git a/javascript/test/node_modules/lib1/package.json b/javascript/test/node_modules/lib1/package.json new file mode 100644 index 0000000..f9ffe92 --- /dev/null +++ b/javascript/test/node_modules/lib1/package.json @@ -0,0 +1,12 @@ +{ + "name": "lib1", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/node_modules/lib2/index.js b/javascript/test/node_modules/lib2/index.js new file mode 100644 index 0000000..f25fd01 --- /dev/null +++ b/javascript/test/node_modules/lib2/index.js @@ -0,0 +1,7 @@ +let lib2_fun = function (){ + console.log("This function is called from lib2"); +} + +module.exports = { + lib2_fun +} diff --git a/javascript/test/node_modules/lib2/package.json b/javascript/test/node_modules/lib2/package.json new file mode 100644 index 0000000..bb36207 --- /dev/null +++ b/javascript/test/node_modules/lib2/package.json @@ -0,0 +1,12 @@ +{ + "name": "lib2", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/one_way_call/library.js b/javascript/test/one_way_call/library.js new file mode 100644 index 0000000..ae9e266 --- /dev/null +++ b/javascript/test/one_way_call/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/one_way_call/node_modules/lib1/index.js b/javascript/test/one_way_call/node_modules/lib1/index.js new file mode 100644 index 0000000..43daf22 --- /dev/null +++ b/javascript/test/one_way_call/node_modules/lib1/index.js @@ -0,0 +1,14 @@ +let lib1_fun = function (){ + console.log("This function is called from lib1"); + fun(); +} + +let fun = function(){ + console.log("Printed from function fun inside lib1"); +} + + + +module.exports = { + lib1_fun +} diff --git a/javascript/test/one_way_call/node_modules/lib1/package.json b/javascript/test/one_way_call/node_modules/lib1/package.json new file mode 100644 index 0000000..f9ffe92 --- /dev/null +++ b/javascript/test/one_way_call/node_modules/lib1/package.json @@ -0,0 +1,12 @@ +{ + "name": "lib1", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/one_way_call/package.json b/javascript/test/one_way_call/package.json new file mode 100644 index 0000000..c2c6e10 --- /dev/null +++ b/javascript/test/one_way_call/package.json @@ -0,0 +1,14 @@ +{ + "name": "one_way_call", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib1": "1.0.0" + } +} diff --git a/javascript/test/one_way_call/script.js b/javascript/test/one_way_call/script.js new file mode 100644 index 0000000..21fbf83 --- /dev/null +++ b/javascript/test/one_way_call/script.js @@ -0,0 +1,22 @@ +lib = require('./library'); +lib1 = require('lib1'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; +let are = lib.area; + +// Calling the functions +// defined in the lib module +function fun (){ + are(length, breadth); + lib.perimeter(length, breadth); + lib1.lib1_fun(); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); diff --git a/javascript/test/one_way_call/test_answer.truth b/javascript/test/one_way_call/test_answer.truth new file mode 100644 index 0000000..4b59637 --- /dev/null +++ b/javascript/test/one_way_call/test_answer.truth @@ -0,0 +1,84 @@ +{ + "data": [ + [ + "one_way_call/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "one_way_call/script.js/fun_at_14:0", + 15 + ] + ] + ], + [ + "one_way_call/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "one_way_call/script.js/fun_at_14:0", + 16 + ] + ] + ], + [ + "lib1/index.js/anonymous_at_6:10", + false, + false, + "-", + "index.js", + 6, + 8, + [ + [ + "one_way_call/script.js/global", + 20 + ], + [ + "lib1/index.js/anonymous_at_1:15", + 3 + ] + ] + ], + [ + "one_way_call/script.js/fun_at_14:0", + true, + false, + "-", + "script.js", + 14, + 18, + [ + [ + "one_way_call/script.js/global", + 20 + ] + ] + ], + [ + "lib1/index.js/anonymous_at_1:15", + false, + false, + "-", + "index.js", + 1, + 4, + [ + [ + "one_way_call/script.js/fun_at_14:0", + 17 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/overload/overload.js b/javascript/test/overload/overload.js new file mode 100644 index 0000000..a9b2906 --- /dev/null +++ b/javascript/test/overload/overload.js @@ -0,0 +1,12 @@ +function func() { + return 37; +} + +function func(a) { + return a; +} + +function main() { + func(); +} + diff --git a/javascript/test/overload/package.json b/javascript/test/overload/package.json new file mode 100644 index 0000000..b181646 --- /dev/null +++ b/javascript/test/overload/package.json @@ -0,0 +1,12 @@ +{ + "name": "overload", + "version": "1.0.0", + "description": "", + "main": "overload.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/recursive_dependencies/library.js b/javascript/test/recursive_dependencies/library.js new file mode 100644 index 0000000..ae9e266 --- /dev/null +++ b/javascript/test/recursive_dependencies/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/recursive_dependencies/node_modules/lib1/index.js b/javascript/test/recursive_dependencies/node_modules/lib1/index.js new file mode 100644 index 0000000..82f9980 --- /dev/null +++ b/javascript/test/recursive_dependencies/node_modules/lib1/index.js @@ -0,0 +1,10 @@ +lib2 = require('lib2'); + +let lib1_fun = function (){ + console.log("This function is called from lib1 that is dependent on lib2"); + lib2.lib2_fun(); +} + +module.exports = { + lib1_fun +} diff --git a/javascript/test/recursive_dependencies/node_modules/lib1/node_modules/lib2/index.js b/javascript/test/recursive_dependencies/node_modules/lib1/node_modules/lib2/index.js new file mode 100644 index 0000000..dac0799 --- /dev/null +++ b/javascript/test/recursive_dependencies/node_modules/lib1/node_modules/lib2/index.js @@ -0,0 +1,15 @@ +lib1 = require('lib1'); + +let lib2_fun = function (){ + console.log("This function is called from lib2 that is dependent on lib1"); + +} + +let lib2_fun2 = function(){ + console.log("This function calls lib1_fun from lib2"); + lib1.lib1_fun(); +} + +module.exports = { + lib2_fun +} diff --git a/javascript/test/recursive_dependencies/node_modules/lib1/node_modules/lib2/package.json b/javascript/test/recursive_dependencies/node_modules/lib1/node_modules/lib2/package.json new file mode 100644 index 0000000..910f758 --- /dev/null +++ b/javascript/test/recursive_dependencies/node_modules/lib1/node_modules/lib2/package.json @@ -0,0 +1,14 @@ +{ + "name": "lib2", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib1": "1.0.0" + } +} diff --git a/javascript/test/recursive_dependencies/node_modules/lib1/package.json b/javascript/test/recursive_dependencies/node_modules/lib1/package.json new file mode 100644 index 0000000..a415342 --- /dev/null +++ b/javascript/test/recursive_dependencies/node_modules/lib1/package.json @@ -0,0 +1,14 @@ +{ + "name": "lib1", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib2": "1.0.0" + } +} diff --git a/javascript/test/recursive_dependencies/package.json b/javascript/test/recursive_dependencies/package.json new file mode 100644 index 0000000..c31abf6 --- /dev/null +++ b/javascript/test/recursive_dependencies/package.json @@ -0,0 +1,14 @@ +{ + "name": "recursive_dependencies", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "lib1": "1.0.0" + } +} diff --git a/javascript/test/recursive_dependencies/script.js b/javascript/test/recursive_dependencies/script.js new file mode 100644 index 0000000..21fbf83 --- /dev/null +++ b/javascript/test/recursive_dependencies/script.js @@ -0,0 +1,22 @@ +lib = require('./library'); +lib1 = require('lib1'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; +let are = lib.area; + +// Calling the functions +// defined in the lib module +function fun (){ + are(length, breadth); + lib.perimeter(length, breadth); + lib1.lib1_fun(); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); diff --git a/javascript/test/recursive_dependencies/test_answer.truth b/javascript/test/recursive_dependencies/test_answer.truth new file mode 100644 index 0000000..dc9b25b --- /dev/null +++ b/javascript/test/recursive_dependencies/test_answer.truth @@ -0,0 +1,84 @@ +{ + "data": [ + [ + "recursive_dependencies/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "recursive_dependencies/script.js/fun_at_14:0", + 15 + ] + ] + ], + [ + "recursive_dependencies/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "recursive_dependencies/script.js/fun_at_14:0", + 16 + ] + ] + ], + [ + "recursive_dependencies/script.js/fun_at_14:0", + true, + false, + "-", + "script.js", + 14, + 18, + [ + [ + "recursive_dependencies/script.js/global", + 20 + ] + ] + ], + [ + "lib1/index.js/anonymous_at_3:15", + false, + false, + "-", + "index.js", + 3, + 6, + [ + [ + "lib2/index.js/anonymous_at_8:16", + 10 + ], + [ + "recursive_dependencies/script.js/fun_at_14:0", + 17 + ] + ] + ], + [ + "lib2/index.js/anonymous_at_3:15", + false, + false, + "-", + "index.js", + 3, + 6, + [ + [ + "lib1/index.js/anonymous_at_3:15", + 5 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/simple_module/node_mod/library.js b/javascript/test/simple_module/node_mod/library.js new file mode 100644 index 0000000..ae9e266 --- /dev/null +++ b/javascript/test/simple_module/node_mod/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/simple_module/package.json b/javascript/test/simple_module/package.json new file mode 100644 index 0000000..ecddba0 --- /dev/null +++ b/javascript/test/simple_module/package.json @@ -0,0 +1,12 @@ +{ + "name": "simple_module", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/simple_module/script.js b/javascript/test/simple_module/script.js new file mode 100644 index 0000000..1089040 --- /dev/null +++ b/javascript/test/simple_module/script.js @@ -0,0 +1,25 @@ +// Importing the module library containing +// area and perimeter functions. +// " ./ " is used if both the files are in the same folder. +const lib = require('./node_mod/library'); +// const leftPad = require('left-pad'); +// const cryptoJs = require('crypto-js'); +// var diff = require('arr-diff'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; + +// Calling the functions +// defined in the lib module +function fun (){ + lib.area(length, breadth); + lib.perimeter(length, breadth); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); \ No newline at end of file diff --git a/javascript/test/simple_module/test_answer.truth b/javascript/test/simple_module/test_answer.truth new file mode 100644 index 0000000..e879c5f --- /dev/null +++ b/javascript/test/simple_module/test_answer.truth @@ -0,0 +1,50 @@ +{ + "data": [ + [ + "simple_module/script.js/fun_at_18:0", + true, + false, + "-", + "script.js", + 18, + 21, + [ + [ + "simple_module/script.js/global", + 23 + ] + ] + ], + [ + "simple_module/node_mod/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "simple_module/script.js/fun_at_18:0", + 19 + ] + ] + ], + [ + "simple_module/node_mod/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "simple_module/script.js/fun_at_18:0", + 20 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/simple_module_2/node_mod/library.js b/javascript/test/simple_module_2/node_mod/library.js new file mode 100644 index 0000000..4da414f --- /dev/null +++ b/javascript/test/simple_module_2/node_mod/library.js @@ -0,0 +1,3 @@ +module.exports = function fun2(){ + console.log('Printed by function declared in export'); +}; diff --git a/javascript/test/simple_module_2/package.json b/javascript/test/simple_module_2/package.json new file mode 100644 index 0000000..c35538e --- /dev/null +++ b/javascript/test/simple_module_2/package.json @@ -0,0 +1,12 @@ +{ + "name": "simple_module_2", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/simple_module_2/script.js b/javascript/test/simple_module_2/script.js new file mode 100644 index 0000000..8598369 --- /dev/null +++ b/javascript/test/simple_module_2/script.js @@ -0,0 +1,5 @@ +// Importing the module library containing +// area and perimeter functions. +const lib = require('./node_mod/library'); + +lib(); diff --git a/javascript/test/simple_module_docstring/node_mod/library.js b/javascript/test/simple_module_docstring/node_mod/library.js new file mode 100644 index 0000000..ae9e266 --- /dev/null +++ b/javascript/test/simple_module_docstring/node_mod/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/simple_module_docstring/package.json b/javascript/test/simple_module_docstring/package.json new file mode 100644 index 0000000..ecddba0 --- /dev/null +++ b/javascript/test/simple_module_docstring/package.json @@ -0,0 +1,12 @@ +{ + "name": "simple_module", + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/simple_module_docstring/script.js b/javascript/test/simple_module_docstring/script.js new file mode 100644 index 0000000..287e1c0 --- /dev/null +++ b/javascript/test/simple_module_docstring/script.js @@ -0,0 +1,37 @@ +// Importing the module library containing +// area and perimeter functions. +// " ./ " is used if both the files are in the same folder. +const lib = require('./node_mod/library'); +// const leftPad = require('left-pad'); +// const cryptoJs = require('crypto-js'); +// var diff = require('arr-diff'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; + +// Calling the functions +// defined in the lib module + +/** + * This is a long docstring that might cause parsing problems. + * + * @return {void} - something. + * @param {nothing} target - blablabla. + * @param {nothing} appName - something something + * @param {function(Error)} callback - this is fake news + * sdfkjsd + */ + + +function fun (){ + lib.area(length, breadth); + lib.perimeter(length, breadth); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); \ No newline at end of file diff --git a/javascript/test/simple_module_no_name/node_mod/library.js b/javascript/test/simple_module_no_name/node_mod/library.js new file mode 100644 index 0000000..ae9e266 --- /dev/null +++ b/javascript/test/simple_module_no_name/node_mod/library.js @@ -0,0 +1,20 @@ +// Area function +let area = function (length, breadth) { + let a = length * breadth; + console.log('Area of the rectangle is ' + a + ' square unit'); +} + +// Perimeter function +let perimeter = function (length, breadth) { + let p = 2 * (length + breadth); + console.log('Perimeter of the rectangle is ' + p + ' unit'); +} + +// Making all functions available in this +// module to exports that we have made +// so that we can import this module and +// use these functions whenever we want. +module.exports = { + area, + perimeter +} \ No newline at end of file diff --git a/javascript/test/simple_module_no_name/package.json b/javascript/test/simple_module_no_name/package.json new file mode 100644 index 0000000..57bbfc7 --- /dev/null +++ b/javascript/test/simple_module_no_name/package.json @@ -0,0 +1,11 @@ +{ + "version": "1.0.0", + "description": "", + "main": "script.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/simple_module_no_name/script.js b/javascript/test/simple_module_no_name/script.js new file mode 100644 index 0000000..1089040 --- /dev/null +++ b/javascript/test/simple_module_no_name/script.js @@ -0,0 +1,25 @@ +// Importing the module library containing +// area and perimeter functions. +// " ./ " is used if both the files are in the same folder. +const lib = require('./node_mod/library'); +// const leftPad = require('left-pad'); +// const cryptoJs = require('crypto-js'); +// var diff = require('arr-diff'); + +var a = ['a', 'b', 'c', 'd']; +var b = ['b', 'c']; + +// console.log(diff(a, b)); +let length = 10; +let breadth = 5; + +// Calling the functions +// defined in the lib module +function fun (){ + lib.area(length, breadth); + lib.perimeter(length, breadth); +} + +fun(); +// console.log(leftPad(17, 5, 0)); +// console.log(cryptoJs.SHA256('message')); \ No newline at end of file diff --git a/javascript/test/simple_module_no_name/test_answer.truth b/javascript/test/simple_module_no_name/test_answer.truth new file mode 100644 index 0000000..cbe739a --- /dev/null +++ b/javascript/test/simple_module_no_name/test_answer.truth @@ -0,0 +1,50 @@ +{ + "data": [ + [ + "simple_module_no_name/script.js/fun_at_18:0", + true, + false, + "-", + "script.js", + 18, + 21, + [ + [ + "simple_module_no_name/script.js/global", + 23 + ] + ] + ], + [ + "simple_module_no_name/node_mod/library.js/anonymous_at_2:11", + true, + false, + "-", + "library.js", + 2, + 5, + [ + [ + "simple_module_no_name/script.js/fun_at_18:0", + 19 + ] + ] + ], + [ + "simple_module_no_name/node_mod/library.js/anonymous_at_8:16", + true, + false, + "-", + "library.js", + 8, + 11, + [ + [ + "simple_module_no_name/script.js/fun_at_18:0", + 20 + ] + ] + ] + ], + "version": 2 +} \ No newline at end of file diff --git a/javascript/test/test_acg.py b/javascript/test/test_acg.py new file mode 100644 index 0000000..3ba90c1 --- /dev/null +++ b/javascript/test/test_acg.py @@ -0,0 +1,250 @@ +import os +import json +import pytest +import subprocess + +sp = os.path.dirname(os.path.abspath(__file__)) + "/" + +def test_simple_module(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "simple_module/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + + truth_file = os.path.join(sp, "simple_module", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +@pytest.mark.xfail +def test_simple_module_2(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "simple_module_2/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + + truth_file = os.path.join(sp, "simple_module_2", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_basics_assignment(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "basics_assignment/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + + + truth_file = os.path.join(sp, "basics_assignment", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_basics_arrow(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "basics_arrow/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print(cg) + + truth_file = os.path.join(sp, "basics_arrow", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_basics_global_as_prop(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "basics_global-as-prop/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print(cg) + + truth_file = os.path.join(sp, "basics_global-as-prop", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_basics_local_is_fine(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "basics_local_is_fine/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print(cg) + + truth_file = os.path.join(sp, "basics_local_is_fine", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_basics_method_def(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "basics_method_def/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + + print(cg) + + truth_file = os.path.join(sp, "basics_method_def", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +@pytest.mark.xfail +def test_unhandled_classes_class_getter(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "unhandled_classes_class_getter/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + + truth_file = os.path.join(sp, "unhandled_classes_class_getter", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +@pytest.mark.xfail +def test_unhandled_limits_history(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "unhandled_limits_history/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + + truth_file = os.path.join(sp, "unhandled_limits_history", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +@pytest.mark.xfail +def test_overload(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "overload/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print(cg) + + truth_file = os.path.join(sp, "overload", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_es6_module(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "es6_module", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print(cg) + + truth_file = os.path.join(sp, "es6_module", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") diff --git a/javascript/test/test_gen_package_cg.py b/javascript/test/test_gen_package_cg.py new file mode 100644 index 0000000..1d7c03b --- /dev/null +++ b/javascript/test/test_gen_package_cg.py @@ -0,0 +1,183 @@ +import sys +import os +import json +import subprocess + +sp = os.path.dirname(os.path.abspath(__file__)) + "/" + +def test_import_from_inside(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "import_from_inside/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print("cg:", cg) + + truth_file = os.path.join(sp, "import_from_inside", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_import_from_outside(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "import_from_outside/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print("cg:", cg) + + truth_file = os.path.join(sp, "import_from_outside", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_inside_priority(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "import_priority/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print("cg:", cg) + + truth_file = os.path.join(sp, "import_priority", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_ignore(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "ignore_specific_keyword/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print("cg:", cg) + + truth_file = os.path.join(sp, "ignore_specific_keyword", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_recursive_dependencies(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "recursive_dependencies/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print("cg:", cg) + + truth_file = os.path.join(sp, "recursive_dependencies", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_incorrect_script(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "incorrect_dep/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print("cg:", cg) + + truth_file = os.path.join(sp, "incorrect_dep", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + +def test_one_way_call(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "one_way_call", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print("cg:", cg) + + truth_file = os.path.join(sp, "one_way_call", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") + + +def test_no_name(): + + cmd = ["python", sp + "../src/gen_package_cg.py", "-i", sp + "simple_module_no_name/", "cg.json"] + subprocess.run(cmd) + + with open("cg.json", "r") as f: + cg = json.load(f) + print("cg:", cg) + + truth_file = os.path.join(sp, "simple_module_no_name", "test_answer.truth") + print(truth_file) + assert os.path.isfile(truth_file), "Truth file missing" + with open(truth_file, "r") as f: + corr_cg = json.load(f) + for i in range(len(cg['data'])): + cg['data'][i][-1].sort(key= lambda x: x[0]) + corr_cg['data'][i][-1].sort(key= lambda x: x[0]) + assert sorted(cg['data'], key = lambda x: x[0]) == sorted(corr_cg['data'], key = lambda x: x[0]) + + if os.path.isfile("cg.json"): + os.remove("cg.json") \ No newline at end of file diff --git a/javascript/test/unhandled_classes_class_getter/class-getter.js b/javascript/test/unhandled_classes_class_getter/class-getter.js new file mode 100644 index 0000000..0a50fbb --- /dev/null +++ b/javascript/test/unhandled_classes_class_getter/class-getter.js @@ -0,0 +1,18 @@ +class Rectangle { + constructor(height, width) { + this.height = height; + this.width = width; + } + // Getter + get area() { + return this.calc_area(); + } + // Method + calc_area() { + return this.height * this.width; + } +} + +const square = new Rectangle(12, 5); + +console.log(square.area); diff --git a/javascript/test/unhandled_classes_class_getter/package.json b/javascript/test/unhandled_classes_class_getter/package.json new file mode 100644 index 0000000..66cf56f --- /dev/null +++ b/javascript/test/unhandled_classes_class_getter/package.json @@ -0,0 +1,12 @@ +{ + "name": "unhandled_classes_class_getter", + "version": "1.0.0", + "description": "", + "main": "class-getter.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/javascript/test/unhandled_limits_history/history.js b/javascript/test/unhandled_limits_history/history.js new file mode 100644 index 0000000..5535af9 --- /dev/null +++ b/javascript/test/unhandled_limits_history/history.js @@ -0,0 +1,10 @@ +function main_func () { + let a = function a_func () { console.log('a_func is called!'); }; + let b = a; + b = function b_func() { console.log('b_func is called!'); }; + a = b; + a(); // b_func is called + b(); // b_func is called +} + +main_func(); diff --git a/javascript/test/unhandled_limits_history/package.json b/javascript/test/unhandled_limits_history/package.json new file mode 100644 index 0000000..235e116 --- /dev/null +++ b/javascript/test/unhandled_limits_history/package.json @@ -0,0 +1,12 @@ +{ + "name": "unhandled_limits_history", + "version": "1.0.0", + "description": "", + "main": "history.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +}