diff --git a/.github/workflows/code_scanning.yml b/.github/workflows/code_scanning.yml index adcb4e92..357bdb5f 100644 --- a/.github/workflows/code_scanning.yml +++ b/.github/workflows/code_scanning.yml @@ -10,10 +10,6 @@ on: - cron: '39 12 * * 2' workflow_dispatch: -env: - LGTM_INDEX_XML_MODE: all - LGTM_INDEX_FILETYPES: ".json:JSON\n.cds:JSON" - jobs: analyze-javascript: name: Analyze @@ -37,40 +33,36 @@ jobs: mv $dir .github/codeql/extensions/$dir done - - name: Ensure presence of cds shell command - run: | - if ! command -v cds &> /dev/null - then - npm install -g @sap/cds-dk - fi - - # Compile .cds files to .cds.json files. - - name: Compile CAP CDS files - run: | - for cds_file in $(find . -type f \( -iname '*.cds' \) -print) - do - echo "I am compiling $cds_file" - cds compile $cds_file \ - -2 json \ - -o "$cds_file.json" \ - --locations - done - - name: Extract CodeQL bundle version from qlt.conf.json run: | echo "BUNDLE_VERSION=$(jq .CodeQLCLIBundle qlt.conf.json -r)" >> $GITHUB_ENV - name: Initialize CodeQL + id: initialize-codeql uses: github/codeql-action/init@v3 + env: + # Add our custom extractor to the CodeQL search path + CODEQL_ACTION_EXTRA_OPTIONS: '{"database":{"init":["--search-path","${{ github.workspace }}/extractors"]}}' with: languages: javascript config-file: ./.github/codeql/codeql-config.yaml + db-location: ${{ runner.temp }}/codeql-database tools: https://github.com/github/codeql-action/releases/download/${{env.BUNDLE_VERSION}}/codeql-bundle-linux64.tar.gz debug: true + - name: Run CDS extractor + shell: bash + run: | + export CODEQL_DIST="$(dirname "${{ steps.initialize-codeql.outputs.codeql-path }}")" + export CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE="${{ runner.temp }}/codeql-database/javascript" + ${{ github.workspace }}/scripts/compile-cds.sh + - name: Perform CodeQL Analysis id: analyze uses: github/codeql-action/analyze@v3 + env: + LGTM_INDEX_XML_MODE: all + LGTM_INDEX_FILETYPES: ".json:JSON" - name: Setup Python uses: actions/setup-python@v5 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..665fde68 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# This repository is maintained by: +@es-codeql \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6dc4b121 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at . All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..543d3c4c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +## Contributing + +[fork]: https://github.com/advanced-security/codeql-sap-js/fork +[pr]: https://github.com/advanced-security/codeql-sap-js/compare +[style]: https://github.com/github/codeql/blob/main/docs/ql-style-guide.md + +Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. + +Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE.txt). + +Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. + +## Submitting a pull request + +1. [Fork][fork] and clone the repository +1. Configure and install the [CodeQL CLI](https://github.com/github/codeql-cli-binaries/releases) specified in the `qlt.conf.json` file +1. Create a new branch: `git checkout -b my-branch-name` +1. Make your changes +1. Make sure the QL tests pass on your machine +1. Ensure the files are appropriately formatted (QL files should be formatted with `codeql query format`) +1. Push to your fork and [submit a draft pull request](https://github.com/advanced-security/codeql-sap-js/compare). Make sure to select **Create Draft Pull Request**. +7. Address failed checks, if any. +8. Mark the [pull request ready for review](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review). +9. Pat your self on the back and wait for your pull request to be reviewed and merged. + +Here are a few things you can do that will increase the likelihood of your pull request being accepted: + +- Follow the [CodeQL style guide][style]. +- Write good tests. +- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests. +- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +## Resources + +- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) +- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) +- [GitHub Help](https://help.github.com) diff --git a/LICENSE b/LICENSE.txt similarity index 95% rename from LICENSE rename to LICENSE.txt index 0d381fb9..28a50fa2 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 GitHub Advanced Security +Copyright GitHub, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 81f7f23c..70dcd788 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,15 @@ codeql database analyze --format=sarif-latest --output= \ advanced-security/javascript-sap-async-xsjs-queries ``` -## License +## License + +This project is licensed under the terms of the MIT open source license. Please refer to [MIT](LICENSE.txt) for the full terms. + +## Maintainers + +See [CODEOWNERS](CODEOWNERS) + +## Support + +See [SUPPORT](SUPPORT.md) -The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..67a9cbf2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,31 @@ +Thanks for helping make GitHub safe for everyone. + +# Security + +GitHub takes the security of our software products and services seriously, including all of the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub). + +Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we will ensure that your finding gets passed along to the appropriate maintainers for remediation. + +## Reporting Security Issues + +If you believe you have found a security vulnerability in any GitHub-owned repository, please report it to us through coordinated disclosure. + +**Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** + +Instead, please send an email to opensource-security[@]github.com. + +Please include as much of the information listed below as you can to help us better understand and resolve the issue: + + * The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Policy + +See [GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms) diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 00000000..14936125 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,13 @@ +# Support + +## How to file issues and get help + +This project uses GitHub issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue. + +For help or questions about using this project, please raise an issue. + +CodeQL for SAP JavaScript frameworks is under active development and maintained by GitHub staff AND THE COMMUNITY. We will do our best to respond to support, feature requests, and community questions in a timely manner. + +## GitHub Support Policy + +Support for this project is limited to the resources listed above. diff --git a/extractors/cds/cds.dbscheme b/extractors/cds/cds.dbscheme new file mode 100644 index 00000000..d46f76c6 --- /dev/null +++ b/extractors/cds/cds.dbscheme @@ -0,0 +1,1190 @@ +/*** Standard fragments ***/ + +/*- Files and folders -*/ + +/** + * The location of an element. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref + ); + + files( + unique int id: @file, + string name: string ref + ); + + folders( + unique int id: @folder, + string name: string ref + ); + + @container = @file | @folder + + containerparent( + int parent: @container ref, + unique int child: @container ref + ); + + /*- Lines of code -*/ + + numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref + ); + + /*- External data -*/ + + /** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ + externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref + ); + + /*- Source location prefix -*/ + + /** + * The source location of the snapshot. + */ + sourceLocationPrefix(string prefix : string ref); + + /*- JavaScript-specific part -*/ + + @location = @location_default + + @sourceline = @locatable; + + filetype( + int file: @file ref, + string filetype: string ref + ) + + // top-level code fragments + toplevels (unique int id: @toplevel, + int kind: int ref); + + is_externs (int toplevel: @toplevel ref); + + case @toplevel.kind of + 0 = @script + | 1 = @inline_script + | 2 = @event_handler + | 3 = @javascript_url + | 4 = @template_toplevel; + + is_module (int tl: @toplevel ref); + is_nodejs (int tl: @toplevel ref); + is_es2015_module (int tl: @toplevel ref); + is_closure_module (int tl: @toplevel ref); + + @xml_node_with_code = @xmlelement | @xmlattribute | @template_placeholder_tag; + toplevel_parent_xml_node( + unique int toplevel: @toplevel ref, + int xmlnode: @xml_node_with_code ref); + + xml_element_parent_expression( + unique int xmlnode: @xmlelement ref, + int expression: @expr ref, + int index: int ref); + + // statements + #keyset[parent, idx] + stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmt_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + + stmt_containers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + + jump_targets (unique int jump: @stmt ref, + int target: @stmt ref); + + @stmt_parent = @stmt | @toplevel | @function_expr | @arrow_function_expr | @static_initializer; + @stmt_container = @toplevel | @function | @namespace_declaration | @external_module_declaration | @global_augmentation_declaration; + + case @stmt.kind of + 0 = @empty_stmt + | 1 = @block_stmt + | 2 = @expr_stmt + | 3 = @if_stmt + | 4 = @labeled_stmt + | 5 = @break_stmt + | 6 = @continue_stmt + | 7 = @with_stmt + | 8 = @switch_stmt + | 9 = @return_stmt + | 10 = @throw_stmt + | 11 = @try_stmt + | 12 = @while_stmt + | 13 = @do_while_stmt + | 14 = @for_stmt + | 15 = @for_in_stmt + | 16 = @debugger_stmt + | 17 = @function_decl_stmt + | 18 = @var_decl_stmt + | 19 = @case + | 20 = @catch_clause + | 21 = @for_of_stmt + | 22 = @const_decl_stmt + | 23 = @let_stmt + | 24 = @legacy_let_stmt + | 25 = @for_each_stmt + | 26 = @class_decl_stmt + | 27 = @import_declaration + | 28 = @export_all_declaration + | 29 = @export_default_declaration + | 30 = @export_named_declaration + | 31 = @namespace_declaration + | 32 = @import_equals_declaration + | 33 = @export_assign_declaration + | 34 = @interface_declaration + | 35 = @type_alias_declaration + | 36 = @enum_declaration + | 37 = @external_module_declaration + | 38 = @export_as_namespace_declaration + | 39 = @global_augmentation_declaration + | 40 = @using_decl_stmt + ; + + @decl_stmt = @var_decl_stmt | @const_decl_stmt | @let_stmt | @legacy_let_stmt | @using_decl_stmt; + + @export_declaration = @export_all_declaration | @export_default_declaration | @export_named_declaration; + + @namespace_definition = @namespace_declaration | @enum_declaration; + @type_definition = @class_definition | @interface_declaration | @enum_declaration | @type_alias_declaration | @enum_member; + + is_instantiated(unique int decl: @namespace_declaration ref); + + @declarable_node = @decl_stmt | @namespace_declaration | @class_decl_stmt | @function_decl_stmt | @enum_declaration | @external_module_declaration | @global_augmentation_declaration | @field; + has_declare_keyword(unique int stmt: @declarable_node ref); + + is_for_await_of(unique int forof: @for_of_stmt ref); + + // expressions + #keyset[parent, idx] + exprs (unique int id: @expr, + int kind: int ref, + int parent: @expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + + literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @expr_or_type ref); + + enclosing_stmt (unique int expr: @expr_or_type ref, + int stmt: @stmt ref); + + expr_containers (unique int expr: @expr_or_type ref, + int container: @stmt_container ref); + + array_size (unique int ae: @arraylike ref, + int sz: int ref); + + is_delegating (int yield: @yield_expr ref); + + @expr_or_stmt = @expr | @stmt; + @expr_or_type = @expr | @typeexpr; + @expr_parent = @expr_or_stmt | @property | @function_typeexpr; + @arraylike = @array_expr | @array_pattern; + @type_annotation = @typeexpr | @jsdoc_type_expr; + @node_in_stmt_container = @cfg_node | @type_annotation | @toplevel; + + case @expr.kind of + 0 = @label + | 1 = @null_literal + | 2 = @boolean_literal + | 3 = @number_literal + | 4 = @string_literal + | 5 = @regexp_literal + | 6 = @this_expr + | 7 = @array_expr + | 8 = @obj_expr + | 9 = @function_expr + | 10 = @seq_expr + | 11 = @conditional_expr + | 12 = @new_expr + | 13 = @call_expr + | 14 = @dot_expr + | 15 = @index_expr + | 16 = @neg_expr + | 17 = @plus_expr + | 18 = @log_not_expr + | 19 = @bit_not_expr + | 20 = @typeof_expr + | 21 = @void_expr + | 22 = @delete_expr + | 23 = @eq_expr + | 24 = @neq_expr + | 25 = @eqq_expr + | 26 = @neqq_expr + | 27 = @lt_expr + | 28 = @le_expr + | 29 = @gt_expr + | 30 = @ge_expr + | 31 = @lshift_expr + | 32 = @rshift_expr + | 33 = @urshift_expr + | 34 = @add_expr + | 35 = @sub_expr + | 36 = @mul_expr + | 37 = @div_expr + | 38 = @mod_expr + | 39 = @bitor_expr + | 40 = @xor_expr + | 41 = @bitand_expr + | 42 = @in_expr + | 43 = @instanceof_expr + | 44 = @logand_expr + | 45 = @logor_expr + | 47 = @assign_expr + | 48 = @assign_add_expr + | 49 = @assign_sub_expr + | 50 = @assign_mul_expr + | 51 = @assign_div_expr + | 52 = @assign_mod_expr + | 53 = @assign_lshift_expr + | 54 = @assign_rshift_expr + | 55 = @assign_urshift_expr + | 56 = @assign_or_expr + | 57 = @assign_xor_expr + | 58 = @assign_and_expr + | 59 = @preinc_expr + | 60 = @postinc_expr + | 61 = @predec_expr + | 62 = @postdec_expr + | 63 = @par_expr + | 64 = @var_declarator + | 65 = @arrow_function_expr + | 66 = @spread_element + | 67 = @array_pattern + | 68 = @object_pattern + | 69 = @yield_expr + | 70 = @tagged_template_expr + | 71 = @template_literal + | 72 = @template_element + | 73 = @array_comprehension_expr + | 74 = @generator_expr + | 75 = @for_in_comprehension_block + | 76 = @for_of_comprehension_block + | 77 = @legacy_letexpr + | 78 = @var_decl + | 79 = @proper_varaccess + | 80 = @class_expr + | 81 = @super_expr + | 82 = @newtarget_expr + | 83 = @named_import_specifier + | 84 = @import_default_specifier + | 85 = @import_namespace_specifier + | 86 = @named_export_specifier + | 87 = @exp_expr + | 88 = @assign_exp_expr + | 89 = @jsx_element + | 90 = @jsx_qualified_name + | 91 = @jsx_empty_expr + | 92 = @await_expr + | 93 = @function_sent_expr + | 94 = @decorator + | 95 = @export_default_specifier + | 96 = @export_namespace_specifier + | 97 = @bind_expr + | 98 = @external_module_reference + | 99 = @dynamic_import + | 100 = @expression_with_type_arguments + | 101 = @prefix_type_assertion + | 102 = @as_type_assertion + | 103 = @export_varaccess + | 104 = @decorator_list + | 105 = @non_null_assertion + | 106 = @bigint_literal + | 107 = @nullishcoalescing_expr + | 108 = @e4x_xml_anyname + | 109 = @e4x_xml_static_attribute_selector + | 110 = @e4x_xml_dynamic_attribute_selector + | 111 = @e4x_xml_filter_expression + | 112 = @e4x_xml_static_qualident + | 113 = @e4x_xml_dynamic_qualident + | 114 = @e4x_xml_dotdotexpr + | 115 = @import_meta_expr + | 116 = @assignlogandexpr + | 117 = @assignlogorexpr + | 118 = @assignnullishcoalescingexpr + | 119 = @template_pipe_ref + | 120 = @generated_code_expr + | 121 = @satisfies_expr + ; + + @varaccess = @proper_varaccess | @export_varaccess; + @varref = @var_decl | @varaccess; + + @identifier = @label | @varref | @type_identifier; + + @literal = @null_literal | @boolean_literal | @number_literal | @string_literal | @regexp_literal | @bigint_literal; + + @propaccess = @dot_expr | @index_expr; + + @invokeexpr = @new_expr | @call_expr; + + @unaryexpr = @neg_expr | @plus_expr | @log_not_expr | @bit_not_expr | @typeof_expr | @void_expr | @delete_expr | @spread_element; + + @equality_test = @eq_expr | @neq_expr | @eqq_expr | @neqq_expr; + + @comparison = @equality_test | @lt_expr | @le_expr | @gt_expr | @ge_expr; + + @binaryexpr = @comparison | @lshift_expr | @rshift_expr | @urshift_expr | @add_expr | @sub_expr | @mul_expr | @div_expr | @mod_expr | @exp_expr | @bitor_expr | @xor_expr | @bitand_expr | @in_expr | @instanceof_expr | @logand_expr | @logor_expr | @nullishcoalescing_expr; + + @assignment = @assign_expr | @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr | @assign_mod_expr | @assign_exp_expr | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr | @assign_or_expr | @assign_xor_expr | @assign_and_expr | @assignlogandexpr | @assignlogorexpr | @assignnullishcoalescingexpr; + + @updateexpr = @preinc_expr | @postinc_expr | @predec_expr | @postdec_expr; + + @pattern = @varref | @array_pattern | @object_pattern; + + @comprehension_expr = @array_comprehension_expr | @generator_expr; + + @comprehension_block = @for_in_comprehension_block | @for_of_comprehension_block; + + @import_specifier = @named_import_specifier | @import_default_specifier | @import_namespace_specifier; + + @exportspecifier = @named_export_specifier | @export_default_specifier | @export_namespace_specifier; + + @type_keyword_operand = @import_declaration | @export_declaration | @import_specifier; + + @type_assertion = @as_type_assertion | @prefix_type_assertion; + + @class_definition = @class_decl_stmt | @class_expr; + @interface_definition = @interface_declaration | @interface_typeexpr; + @class_or_interface = @class_definition | @interface_definition; + + @lexical_decl = @var_decl | @type_decl; + @lexical_access = @varaccess | @local_type_access | @local_var_type_access | @local_namespace_access; + @lexical_ref = @lexical_decl | @lexical_access; + + @e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector; + @e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident; + + expr_contains_template_tag_location( + int expr: @expr ref, + int location: @location ref + ); + + @template_placeholder_tag_parent = @xmlelement | @xmlattribute | @file; + + template_placeholder_tag_info( + unique int node: @template_placeholder_tag, + int parentNode: @template_placeholder_tag_parent ref, + varchar(900) raw: string ref + ); + + // scopes + scopes (unique int id: @scope, + int kind: int ref); + + case @scope.kind of + 0 = @global_scope + | 1 = @function_scope + | 2 = @catch_scope + | 3 = @module_scope + | 4 = @block_scope + | 5 = @for_scope + | 6 = @for_in_scope // for-of scopes work the same as for-in scopes + | 7 = @comprehension_block_scope + | 8 = @class_expr_scope + | 9 = @namespace_scope + | 10 = @class_decl_scope + | 11 = @interface_scope + | 12 = @type_alias_scope + | 13 = @mapped_type_scope + | 14 = @enum_scope + | 15 = @external_module_scope + | 16 = @conditional_type_scope; + + scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + + scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + + // functions + @function = @function_decl_stmt | @function_expr | @arrow_function_expr; + + @parameterized = @function | @catch_clause; + @type_parameterized = @function | @class_or_interface | @type_alias_declaration | @mapped_typeexpr | @infer_typeexpr; + + is_generator (int fun: @function ref); + has_rest_parameter (int fun: @function ref); + is_async (int fun: @function ref); + + // variables and lexically scoped type names + #keyset[scope, name] + variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + + #keyset[scope, name] + local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + + #keyset[scope, name] + local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + + is_arguments_object (int id: @variable ref); + + @lexical_name = @variable | @local_type_name | @local_namespace_name; + + @bind_id = @varaccess | @local_var_type_access; + bind (unique int id: @bind_id ref, + int decl: @variable ref); + + decl (unique int id: @var_decl ref, + int decl: @variable ref); + + @typebind_id = @local_type_access | @export_varaccess; + typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + + @typedecl_id = @type_decl | @var_decl; + typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + + namespacedecl (unique int id: @var_decl ref, + int decl: @local_namespace_name ref); + + @namespacebind_id = @local_namespace_access | @export_varaccess; + namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + + // properties in object literals, property patterns in object patterns, and method declarations in classes + #keyset[parent, index] + properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + + case @property.kind of + 0 = @value_property + | 1 = @property_getter + | 2 = @property_setter + | 3 = @jsx_attribute + | 4 = @function_call_signature + | 5 = @constructor_call_signature + | 6 = @index_signature + | 7 = @enum_member + | 8 = @proper_field + | 9 = @parameter_field + | 10 = @static_initializer + ; + + @property_parent = @obj_expr | @object_pattern | @class_definition | @jsx_element | @interface_definition | @enum_declaration; + @property_accessor = @property_getter | @property_setter; + @call_signature = @function_call_signature | @constructor_call_signature; + @field = @proper_field | @parameter_field; + @field_or_vardeclarator = @field | @var_declarator; + + is_computed (int id: @property ref); + is_method (int id: @property ref); + is_static (int id: @property ref); + is_abstract_member (int id: @property ref); + is_const_enum (int id: @enum_declaration ref); + is_abstract_class (int id: @class_decl_stmt ref); + + has_public_keyword (int id: @property ref); + has_private_keyword (int id: @property ref); + has_protected_keyword (int id: @property ref); + has_readonly_keyword (int id: @property ref); + has_type_keyword (int id: @type_keyword_operand ref); + is_optional_member (int id: @property ref); + has_definite_assignment_assertion (int id: @field_or_vardeclarator ref); + is_optional_parameter_declaration (unique int parameter: @pattern ref); + + #keyset[constructor, param_index] + parameter_fields( + unique int field: @parameter_field ref, + int constructor: @function_expr ref, + int param_index: int ref + ); + + // types + #keyset[parent, idx] + typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref + ); + + case @typeexpr.kind of + 0 = @local_type_access + | 1 = @type_decl + | 2 = @keyword_typeexpr + | 3 = @string_literal_typeexpr + | 4 = @number_literal_typeexpr + | 5 = @boolean_literal_typeexpr + | 6 = @array_typeexpr + | 7 = @union_typeexpr + | 8 = @indexed_access_typeexpr + | 9 = @intersection_typeexpr + | 10 = @parenthesized_typeexpr + | 11 = @tuple_typeexpr + | 12 = @keyof_typeexpr + | 13 = @qualified_type_access + | 14 = @generic_typeexpr + | 15 = @type_label + | 16 = @typeof_typeexpr + | 17 = @local_var_type_access + | 18 = @qualified_var_type_access + | 19 = @this_var_type_access + | 20 = @predicate_typeexpr + | 21 = @interface_typeexpr + | 22 = @type_parameter + | 23 = @plain_function_typeexpr + | 24 = @constructor_typeexpr + | 25 = @local_namespace_access + | 26 = @qualified_namespace_access + | 27 = @mapped_typeexpr + | 28 = @conditional_typeexpr + | 29 = @infer_typeexpr + | 30 = @import_type_access + | 31 = @import_namespace_access + | 32 = @import_var_type_access + | 33 = @optional_typeexpr + | 34 = @rest_typeexpr + | 35 = @bigint_literal_typeexpr + | 36 = @readonly_typeexpr + | 37 = @template_literal_typeexpr + ; + + @typeref = @typeaccess | @type_decl; + @type_identifier = @type_decl | @local_type_access | @type_label | @local_var_type_access | @local_namespace_access; + @typeexpr_parent = @expr | @stmt | @property | @typeexpr; + @literal_typeexpr = @string_literal_typeexpr | @number_literal_typeexpr | @boolean_literal_typeexpr | @bigint_literal_typeexpr; + @typeaccess = @local_type_access | @qualified_type_access | @import_type_access; + @vartypeaccess = @local_var_type_access | @qualified_var_type_access | @this_var_type_access | @import_var_type_access; + @namespace_access = @local_namespace_access | @qualified_namespace_access | @import_namespace_access; + @import_typeexpr = @import_type_access | @import_namespace_access | @import_var_type_access; + + @function_typeexpr = @plain_function_typeexpr | @constructor_typeexpr; + + // types + types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref + ); + + #keyset[parent, idx] + type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref + ); + + case @type.kind of + 0 = @any_type + | 1 = @string_type + | 2 = @number_type + | 3 = @union_type + | 4 = @true_type + | 5 = @false_type + | 6 = @type_reference + | 7 = @object_type + | 8 = @canonical_type_variable_type + | 9 = @typeof_type + | 10 = @void_type + | 11 = @undefined_type + | 12 = @null_type + | 13 = @never_type + | 14 = @plain_symbol_type + | 15 = @unique_symbol_type + | 16 = @objectkeyword_type + | 17 = @intersection_type + | 18 = @tuple_type + | 19 = @lexical_type_variable_type + | 20 = @this_type + | 21 = @number_literal_type + | 22 = @string_literal_type + | 23 = @unknown_type + | 24 = @bigint_type + | 25 = @bigint_literal_type + ; + + @boolean_literal_type = @true_type | @false_type; + @symbol_type = @plain_symbol_type | @unique_symbol_type; + @union_or_intersection_type = @union_type | @intersection_type; + @typevariable_type = @canonical_type_variable_type | @lexical_type_variable_type; + + has_asserts_keyword(int node: @predicate_typeexpr ref); + + @typed_ast_node = @expr | @typeexpr | @function; + ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + + declared_function_signature( + unique int node: @function ref, + int sig: @signature_type ref + ); + + invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref + ); + + invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref + ); + + symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref + ); + + symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref + ); + + symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref + ); + + symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref + ); + + case @symbol.kind of + 0 = @root_symbol + | 1 = @member_symbol + | 2 = @other_symbol + ; + + @type_with_symbol = @type_reference | @typevariable_type | @typeof_type | @unique_symbol_type; + @ast_node_with_symbol = @type_definition | @namespace_definition | @toplevel | @typeaccess | @namespace_access | @var_decl | @function | @invokeexpr | @import_declaration | @external_module_reference | @external_module_declaration; + + ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + + type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + + #keyset[typ, name] + type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + + type_alias( + unique int aliasType: @type ref, + int underlyingType: @type ref); + + @literal_type = @string_literal_type | @number_literal_type | @boolean_literal_type | @bigint_literal_type; + @type_with_literal_value = @string_literal_type | @number_literal_type | @bigint_literal_type; + type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + + signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref + ); + + is_abstract_signature( + unique int sig: @signature_type ref + ); + + signature_rest_parameter( + unique int sig: @signature_type ref, + int rest_param_arra_type: @type ref + ); + + case @signature_type.kind of + 0 = @function_signature_type + | 1 = @constructor_signature_type + ; + + #keyset[typ, kind, index] + type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref + ); + + #keyset[parent, index] + signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref + ); + + #keyset[sig, index] + signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref + ); + + number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref + ); + + string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref + ); + + base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref + ); + + self_types( + int typeName: @symbol ref, + int selfType: @type_reference ref + ); + + tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref + ); + + tuple_type_rest_index( + unique int typ: @type ref, + int index: int ref + ); + + // comments + comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + + case @comment.kind of + 0 = @slashslash_comment + | 1 = @slashstar_comment + | 2 = @doc_comment + | 3 = @html_comment_start + | 4 = @htmlcommentend; + + @html_comment = @html_comment_start | @htmlcommentend; + @line_comment = @slashslash_comment | @html_comment; + @block_comment = @slashstar_comment | @doc_comment; + + // source lines + lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); + indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + + // JavaScript parse errors + js_parse_errors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + + // regular expressions + #keyset[parent, idx] + regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + + @regexpparent = @regexpterm | @regexp_literal | @string_literal | @add_expr; + + case @regexpterm.kind of + 0 = @regexp_alt + | 1 = @regexp_seq + | 2 = @regexp_caret + | 3 = @regexp_dollar + | 4 = @regexp_wordboundary + | 5 = @regexp_nonwordboundary + | 6 = @regexp_positive_lookahead + | 7 = @regexp_negative_lookahead + | 8 = @regexp_star + | 9 = @regexp_plus + | 10 = @regexp_opt + | 11 = @regexp_range + | 12 = @regexp_dot + | 13 = @regexp_group + | 14 = @regexp_normal_constant + | 15 = @regexp_hex_escape + | 16 = @regexp_unicode_escape + | 17 = @regexp_dec_escape + | 18 = @regexp_oct_escape + | 19 = @regexp_ctrl_escape + | 20 = @regexp_char_class_escape + | 21 = @regexp_id_escape + | 22 = @regexp_backref + | 23 = @regexp_char_class + | 24 = @regexp_char_range + | 25 = @regexp_positive_lookbehind + | 26 = @regexp_negative_lookbehind + | 27 = @regexp_unicode_property_escape; + + regexp_parse_errors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + + @regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; + @regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; + @regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; + @regexp_constant = @regexp_normal_constant | @regexp_char_escape; + @regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; + @regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; + @regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; + @regexp_anchor = @regexp_dollar | @regexp_caret; + + is_greedy (int id: @regexp_quantifier ref); + range_quantifier_lower_bound (unique int id: @regexp_range ref, int lo: int ref); + range_quantifier_upper_bound (unique int id: @regexp_range ref, int hi: int ref); + is_capture (unique int id: @regexp_group ref, int number: int ref); + is_named_capture (unique int id: @regexp_group ref, string name: string ref); + is_inverted (int id: @regexp_char_class ref); + regexp_const_value (unique int id: @regexp_constant ref, varchar(1) value: string ref); + char_class_escape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); + backref (unique int id: @regexp_backref ref, int value: int ref); + named_backref (unique int id: @regexp_backref ref, string name: string ref); + unicode_property_escapename (unique int id: @regexp_unicode_property_escape ref, string name: string ref); + unicode_property_escapevalue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + + // tokens + #keyset[toplevel, idx] + tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + + case @token.kind of + 0 = @token_eof + | 1 = @token_null_literal + | 2 = @token_boolean_literal + | 3 = @token_numeric_literal + | 4 = @token_string_literal + | 5 = @token_regular_expression + | 6 = @token_identifier + | 7 = @token_keyword + | 8 = @token_punctuator; + + // associate comments with the token immediately following them (which may be EOF) + next_token (int comment: @comment ref, int token: @token ref); + + // JSON + #keyset[parent, idx] + json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + + json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + + json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + + json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + + json_locations(unique int locatable: @json_locatable ref, + int location: @location_default ref); + + case @json_value.kind of + 0 = @json_null + | 1 = @json_boolean + | 2 = @json_number + | 3 = @json_string + | 4 = @json_array + | 5 = @json_object; + + @json_parent = @json_object | @json_array | @file; + + @json_locatable = @json_value | @json_parse_error; + + // locations + @ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + + @locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error + | @regexpterm + | @json_locatable + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_locatable + | @xmllocatable + | @configLocatable + | @template_placeholder_tag; + + hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + + // CFG + entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); + exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); + guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); + case @guard_node.kind of + 0 = @falsy_guard + | 1 = @truthy_guard; + @condition_guard = @falsy_guard | @truthy_guard; + + @synthetic_cfg_node = @entry_node | @exit_node | @guard_node; + @cfg_node = @synthetic_cfg_node | @expr_parent; + + successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + + // JSDoc comments + jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); + #keyset[parent, idx] + jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); + jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + + #keyset[parent, idx] + jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr + | 1 = @jsdoc_null_type_expr + | 2 = @jsdoc_undefined_type_expr + | 3 = @jsdoc_unknown_type_expr + | 4 = @jsdoc_void_type_expr + | 5 = @jsdoc_named_type_expr + | 6 = @jsdoc_applied_type_expr + | 7 = @jsdoc_nullable_type_expr + | 8 = @jsdoc_non_nullable_type_expr + | 9 = @jsdoc_record_type_expr + | 10 = @jsdoc_array_type_expr + | 11 = @jsdoc_union_type_expr + | 12 = @jsdoc_function_type_expr + | 13 = @jsdoc_optional_type_expr + | 14 = @jsdoc_rest_type_expr + ; + + #keyset[id, idx] + jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); + jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); + jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + + @jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + + jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + + @dataflownode = @expr | @function_decl_stmt | @class_decl_stmt | @namespace_declaration | @enum_declaration | @property; + + @optionalchainable = @call_expr | @propaccess; + + isOptionalChaining(int id: @optionalchainable ref); + + /** + * The time taken for the extraction of a file. + * This table contains non-deterministic content. + * + * The sum of the `time` column for each (`file`, `timerKind`) pair + * is the total time taken for extraction of `file`. The `extractionPhase` + * column provides a granular view of the extraction time of the file. + */ + extraction_time( + int file : @file ref, + // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`. + int extractionPhase: int ref, + // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds + int timerKind: int ref, + float time: float ref + ) + + /** + * Non-timing related data for the extraction of a single file. + * This table contains non-deterministic content. + */ + extraction_data( + int file : @file ref, + // the absolute path to the cache file + varchar(900) cacheFile: string ref, + boolean fromCache: boolean ref, + int length: int ref + ) + + /*- YAML -*/ + + #keyset[parent, idx] + yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + string tag: string ref, + string tostring: string ref); + + case @yaml_node.kind of + 0 = @yaml_scalar_node + | 1 = @yaml_mapping_node + | 2 = @yaml_sequence_node + | 3 = @yaml_alias_node + ; + + @yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + + @yaml_node_parent = @yaml_collection_node | @file; + + yaml_anchors (unique int node: @yaml_node ref, + string anchor: string ref); + + yaml_aliases (unique int alias: @yaml_alias_node ref, + string target: string ref); + + yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + string value: string ref); + + yaml_errors (unique int id: @yaml_error, + string message: string ref); + + yaml_locations(unique int locatable: @yaml_locatable ref, + int location: @location_default ref); + + @yaml_locatable = @yaml_node | @yaml_error; + + /*- XML Files -*/ + + xmlEncoding( + unique int id: @file ref, + string encoding: string ref + ); + + xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref + ); + + xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref + ); + + xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref + ); + + xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref + ); + + xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref + ); + + xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref + ); + + xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref + ); + + @xmlparent = @file | @xmlelement; + @xmlnamespaceable = @xmlelement | @xmlattribute; + + xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref + ); + + @xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + + /*- Configuration files with key value pairs -*/ + + configs( + unique int id: @config + ); + + configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref + ); + + configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref + ); + + configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref + ); + + @configLocatable = @config | @configName | @configValue; \ No newline at end of file diff --git a/extractors/cds/codeql-extractor.yml b/extractors/cds/codeql-extractor.yml new file mode 100644 index 00000000..583b3b04 --- /dev/null +++ b/extractors/cds/codeql-extractor.yml @@ -0,0 +1,11 @@ +name: "cds" +display_name: "SAP CAP CDS" +version: 0.0.1 +column_kind: "utf16" +file_types: + - name: cds + display_name: SAP CAP CDS files + extensions: + - .cds +build_modes: + - none \ No newline at end of file diff --git a/extractors/cds/tools/autobuild.sh b/extractors/cds/tools/autobuild.sh new file mode 100755 index 00000000..5396796d --- /dev/null +++ b/extractors/cds/tools/autobuild.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu + +# NOTE: the code below is copied in three places: +# - scripts/compile-cds.sh +# - extractors/cds/tools/autobuild.sh (here) +# - extractors/javascript/tools/pre-finalize.sh +# Any changes should be synchronized between these three places. + +exec "${CODEQL_DIST}/codeql" database index-files \ + --language cds \ + --total-size-limit 10m \ + --include-extension=.cds \ + --prune **/node_modules/**/* \ + --prune **/.eslint/**/* \ + "$CODEQL_EXTRACTOR_CDS_WIP_DATABASE" \ No newline at end of file diff --git a/extractors/cds/tools/index-files.sh b/extractors/cds/tools/index-files.sh new file mode 100755 index 00000000..8fb60461 --- /dev/null +++ b/extractors/cds/tools/index-files.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +set -eu + +echo "Indexing CDS files" + +# Check if the list of files is empty +response_file="$1" + +# If the response_file doesn't exist, terminate: +if [ ! -f "$response_file" ]; then + echo "codeql database index-files --language cds terminated early as response file '$response_file' does not exist. This is because no CDS files were selected or found." + exit 0 +fi + +# If the response_file is empty, terminate +if [ ! -s "$response_file" ]; then + echo "codeql database index-files --language cds terminated early as response file '$response_file' is empty. This is because no CDS files were selected or found." + exit 0 +fi + +# Determine if we have the cds command available, and if not, install the cds development kit +# in the appropriate directories +if ! command -v cds &> /dev/null +then + echo "Pre-installing cds compiler" + + # Find all the directories containing a package.json with a dependency on @sap/cds, where + # the directory contains at least one of the files listed in the response file (e.g. the + # cds files we want to extract). + # + # We then install the cds development kit (@sap/cds-dk) in each directory, which makes the + # `cds` command usable from the npx command within that directory. + # + # Nested package.json files simply cause the package to be installed in the parent node_modules + # directory. + # + # We also ensure we skip node_modules, as we can end up in a recursive loop + find . -type d -name node_modules -prune -false -o -type f \( -iname 'package.json' \) -exec grep -ql '@sap/cds' {} \; -execdir bash -c "grep -q \"^\$(pwd)\(/\|$\)\" \"$response_file\"" \; -execdir bash -c "echo \"Installing @sap/cds-dk into \$(pwd) to enable CDS compilation.\"" \; -execdir npm install --silent @sap/cds-dk \; + + # Use the npx command to dynamically install the cds development kit (@sap/cds-dk) package if necessary, + # which then provides the cds command line tool in directories which are not covered by the package.json + # install command approach above + cds_command="npx -y --package @sap/cds-dk cds" +else + cds_command="cds" +fi + +echo "Processing CDS files to JSON" + +# Run the cds compile command on each file in the response file, outputting the compiled JSON to a file with +# the same name +while IFS= read -r cds_file; do + echo "Processing CDS file $cds_file to:" + $cds_command compile "$cds_file" \ + -2 json \ + -o "$cds_file.json" \ + --locations +done < "$response_file" + +# Check if the JS extractor variables are set, and set them if not +if [ -z "${CODEQL_EXTRACTOR_JAVASCRIPT_ROOT:-}" ]; then + # Find the JavaScript extractor location + export CODEQL_EXTRACTOR_JAVASCRIPT_ROOT="$("$CODEQL_DIST/codeql" resolve extractor --language=javascript)" + + # Set the JAVASCRIPT extractor environment variables to the same as the CDS extractor environment variables + # so that the JS extractor will write to the CDS database + export CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE="$CODEQL_EXTRACTOR_CDS_WIP_DATABASE" + export CODEQL_EXTRACTOR_JAVASCRIPT_DIAGNOSTIC_DIR="$CODEQL_EXTRACTOR_CDS_DIAGNOSTIC_DIR" + export CODEQL_EXTRACTOR_JAVASCRIPT_LOG_DIR="$CODEQL_EXTRACTOR_CDS_LOG_DIR" + export CODEQL_EXTRACTOR_JAVASCRIPT_SCRATCH_DIR="$CODEQL_EXTRACTOR_CDS_SCRATCH_DIR" + export CODEQL_EXTRACTOR_JAVASCRIPT_TRAP_DIR="$CODEQL_EXTRACTOR_CDS_TRAP_DIR" + export CODEQL_EXTRACTOR_JAVASCRIPT_SOURCE_ARCHIVE_DIR="$CODEQL_EXTRACTOR_CDS_SOURCE_ARCHIVE_DIR" +fi + +# Check if LGTM_INDEX_FILTERS is already set +# This typically happens if "paths" or "paths-ignore" are set in the LGTM.yml file +if [ -z "${LGTM_INDEX_FILTERS:-}" ]; then + exclude_filters="" +else + echo $'Found \$LGTM_INDEX_FILTERS already set to:\n'"$LGTM_INDEX_FILTERS" + # If it is set, we will try to honour the paths-ignore filter + # Split by \n and find all the entries that start with exclude, excluding "exclude:**/*" and "exclude:**/*.*" + # and then join them back together with \n + exclude_filters=$'\n'"$(echo "$LGTM_INDEX_FILTERS" | grep '^exclude' | grep -v 'exclude:\*\*/\*\|exclude:\*\*/\*\.\*')" +fi + +# Enable extraction of the cds.json files only +export LGTM_INDEX_FILTERS=$'exclude:**/*.*\ninclude:**/*.cds.json\ninclude:**/*.cds\nexclude:**/node_modules/**/*.*'"$exclude_filters" +echo "Setting \$LGTM_INDEX_FILTERS to:\n$LGTM_INDEX_FILTERS" +export LGTM_INDEX_TYPESCRIPT="NONE" +# Configure to copy over the CDS files as well, by pretending they are JSON +export LGTM_INDEX_FILETYPES=".cds:JSON" +# Ignore the LGTM_INDEX_INCLUDE variable for this purpose, as it may +# refer explicitly to .ts or .js files +unset LGTM_INDEX_INCLUDE + +echo "Extracting the cds.json files" + +# Invoke the JavaScript autobuilder to index the .cds.json files only +"$CODEQL_EXTRACTOR_JAVASCRIPT_ROOT"/tools/autobuild.sh \ No newline at end of file diff --git a/extractors/javascript/README.md b/extractors/javascript/README.md new file mode 100644 index 00000000..bba2a9a2 --- /dev/null +++ b/extractors/javascript/README.md @@ -0,0 +1,7 @@ +# Extension to the JavaScript extractor to support CDS compilation + +This directory contains a `pre-finalize.sh` script that can be dropped into the `tools` directory +of a JavaScript extractor to enable the automatic compilation and extraction of SAP CAP CDS files +during the `codeql database create` process. + +The script requires that the `cds` extractor is available when `codeql database init` is called. This can either be through providing the extractors parent directory using the `--search-path` bundle, or by including the extractor (and this pre-finalize script) in a custom bundle created by the [CodeQL Development Toolkit (qlt)](https://github.com/advanced-security/codeql-development-toolkit). \ No newline at end of file diff --git a/extractors/javascript/tools/pre-finalize.sh b/extractors/javascript/tools/pre-finalize.sh new file mode 100755 index 00000000..ad7b28f2 --- /dev/null +++ b/extractors/javascript/tools/pre-finalize.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -eu + +# NOTE: the code below is copied in three places: +# - scripts/compile-cds.sh +# - extractors/cds/tools/autobuild.sh +# - extractors/javascript/tools/pre-finalize.sh (here) +# Any changes should be synchronized between these three places. + +# Do not extract CDS files if the CODEQL_EXTRACTOR_CDS_SKIP_EXTRACTION environment variable is set +if [ -z "${CODEQL_EXTRACTOR_CDS_SKIP_EXTRACTION:-}" ]; then + # Call the index-files command with the CDS extractor + "$CODEQL_DIST/codeql" database index-files \ + --language cds \ + --total-size-limit 10m \ + --include-extension=.cds \ + --prune **/node_modules/**/* \ + --prune **/.eslint/**/* \ + "$CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE" +fi \ No newline at end of file diff --git a/javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll b/javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll index 93437100..f4811c9f 100644 --- a/javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll +++ b/javascript/frameworks/cap/lib/advanced_security/javascript/frameworks/cap/CDL.qll @@ -7,20 +7,58 @@ import advanced_security.javascript.frameworks.cap.CDS abstract class CdlObject extends JsonObject { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { - exists(Location loc, JsonValue locValue | - loc = this.getLocation() and - locValue = this.getPropValue("$location") and - path = - any(File f | - f.getAbsolutePath() - .matches("%" + locValue.getPropValue("file").getStringValue() + ".json") - ).getAbsolutePath().regexpReplaceAll("\\.json$", "") and - sl = locValue.getPropValue("line").getIntValue() and - sc = locValue.getPropValue("col").getIntValue() and - el = sl + 1 and - ec = 1 - ) + // If the cds.json file has a $location property, then use that, + // otherwise fall back to the cds.json file itself + if exists(this.getPropValue("$location")) + then + exists(Location loc, JsonValue locValue | + loc = this.getLocation() and + locValue = this.getPropValue("$location") and + path = + any(File f | + f.getAbsolutePath() + .matches("%" + locValue.getPropValue("file").getStringValue() + ".json") + ).getAbsolutePath().regexpReplaceAll("\\.json$", "") and + if + not exists(locValue.getPropValue("line")) and + not exists(locValue.getPropValue("col")) + then + // We don't know where this entity starts, so mark the whole file + sl = 0 and + sc = 0 and + el = 0 and + ec = 0 + else ( + sl = locValue.getPropValue("line").getIntValue() and + ( + if exists(locValue.getPropValue("col")) + then sc = locValue.getPropValue("col").getIntValue() + else + // We don't know where this entity starts, so mark the start of the line + sc = 0 + ) and + el = sl and + ( + if exists(getObjectLocationName()) + then + // Currently $locations does not provide an end location. However, we can + // automatically deduce the end location from the length of the name. + ec = sc + getObjectLocationName().length() - 1 + else + // Mark a single character if we cannot predicate the length + ec = sc + 1 + ) + ) + ) + else super.getLocation().hasLocationInfo(path, sl, sc, el, ec) } + + /** + * The name of the object that should be highlighted as the location. + * + * This is used to deduce the length of the location. + */ + string getObjectLocationName() { none() } } private newtype CdlKind = @@ -31,21 +69,26 @@ private newtype CdlKind = CdlFunctionKind(string value) { value = "function" } /** - * Any CDL element, including entities, event, actions, and more. + * A list of CDL definitions, which can include entities, events, actions and more. */ -class CdlDefinition extends CdlObject { - CdlDefinition() { exists(JsonObject root | this = root.getPropValue("definitions")) } +class CdlDefinitions extends CdlObject { + CdlDefinitions() { exists(JsonObject root | this = root.getPropValue("definitions")) } JsonObject getElement(string elementName) { result = this.getPropValue(elementName) } JsonObject getAnElement() { result = this.getElement(_) } } +/** + * A CDL definition element. + */ abstract class CdlElement extends CdlObject { CdlKind kind; string name; - CdlElement() { exists(CdlDefinition definition | this = definition.getElement(name)) } + CdlElement() { exists(CdlDefinitions definitions | this = definitions.getElement(name)) } + + override string getObjectLocationName() { result = getUnqualifiedName() } /** * Gets the name of this CDL element. @@ -215,6 +258,8 @@ class CdlAttribute extends CdlObject { exists(CdlElement entity | this = entity.getPropValue("elements").getPropValue(name)) } + override string getObjectLocationName() { result = getName() } + string getType() { result = this.getPropStringValue("type") } int getLength() { result = this.getPropValue("length").(JsonPrimitiveValue).getIntValue() } diff --git a/javascript/frameworks/cap/test/queries/bad-authn-authz/entities-with-no-authz/entities-exposed-with-no-authz/entities-exposed-with-no-authz.expected b/javascript/frameworks/cap/test/queries/bad-authn-authz/entities-with-no-authz/entities-exposed-with-no-authz/entities-exposed-with-no-authz.expected index 0a226b09..b1b701a8 100644 --- a/javascript/frameworks/cap/test/queries/bad-authn-authz/entities-with-no-authz/entities-exposed-with-no-authz/entities-exposed-with-no-authz.expected +++ b/javascript/frameworks/cap/test/queries/bad-authn-authz/entities-with-no-authz/entities-exposed-with-no-authz/entities-exposed-with-no-authz.expected @@ -1,6 +1,6 @@ -| srv/service1.cds:3:9:4:1 | {\\n ... }\\n } | The CDS service `Service1` is exposed without any authentication. | -| srv/service1.cds:5:10:6:1 | {\\n ... }\\n } | The CDS entity `Service1.Service1Entity` is exposed without any authentication. | -| srv/service1.cds:8:10:9:1 | {\\n ... }\\n } | The CDS action `Service1.send1` is exposed without any authentication. | -| srv/service2.cds:3:9:4:1 | {\\n ... }\\n } | The CDS service `Service2` is exposed without any authentication. | -| srv/service2.cds:5:10:6:1 | {\\n ... }\\n } | The CDS entity `Service2.Service2Entity` is exposed without any authentication. | -| srv/service2.cds:8:10:9:1 | {\\n ... }\\n } | The CDS action `Service2.send2` is exposed without any authentication. | +| srv/service1.cds:3:9:3:16 | {\\n ... }\\n } | The CDS service `Service1` is exposed without any authentication. | +| srv/service1.cds:5:10:5:23 | {\\n ... }\\n } | The CDS entity `Service1.Service1Entity` is exposed without any authentication. | +| srv/service1.cds:8:10:8:14 | {\\n ... }\\n } | The CDS action `Service1.send1` is exposed without any authentication. | +| srv/service2.cds:3:9:3:16 | {\\n ... }\\n } | The CDS service `Service2` is exposed without any authentication. | +| srv/service2.cds:5:10:5:23 | {\\n ... }\\n } | The CDS entity `Service2.Service2Entity` is exposed without any authentication. | +| srv/service2.cds:8:10:8:14 | {\\n ... }\\n } | The CDS action `Service2.send2` is exposed without any authentication. | \ No newline at end of file diff --git a/javascript/frameworks/cap/test/queries/sensitive-exposure/sensitive-exposure.expected b/javascript/frameworks/cap/test/queries/sensitive-exposure/sensitive-exposure.expected index c1efa56f..85d27ac4 100644 --- a/javascript/frameworks/cap/test/queries/sensitive-exposure/sensitive-exposure.expected +++ b/javascript/frameworks/cap/test/queries/sensitive-exposure/sensitive-exposure.expected @@ -5,4 +5,4 @@ nodes edges | sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | #select -| sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | Log entry depends on the $@ field which is annotated as potentially sensitive. | sensitive-exposure.cds:4:5:5:1 | {\\n ... } | name | +| sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | sensitive-exposure.js:9:32:9:42 | Sample.name | Log entry depends on the $@ field which is annotated as potentially sensitive. | sensitive-exposure.cds:4:5:4:8 | {\\n ... } | name | \ No newline at end of file diff --git a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll index 6669b8ff..f427bc9b 100644 --- a/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll +++ b/javascript/frameworks/ui5/lib/advanced_security/javascript/frameworks/ui5/RemoteFlowSources.qll @@ -58,10 +58,6 @@ class ODataServiceModel extends UI5ExternalModel { override string getSourceType() { result = "ODataServiceModel" } ODataServiceModel() { - /* - * e.g. this.getView().setModel(this.getOwnerComponent().getModel("booking_nobatch")) - */ - exists(MethodCallNode setModelCall, CustomController controller | /* * 1. This flows from a DF node corresponding to the parent component's model to the `this.setModel` call diff --git a/scripts/ExtractJSDoc.ql b/scripts/ExtractJSDoc.ql deleted file mode 100644 index 5c659889..00000000 --- a/scripts/ExtractJSDoc.ql +++ /dev/null @@ -1,4 +0,0 @@ -import FortifyTag - -from FortifySinkTag tag -select tag.getYamlRow() diff --git a/scripts/FortifyTag.qll b/scripts/FortifyTag.qll deleted file mode 100644 index 729c46b9..00000000 --- a/scripts/FortifyTag.qll +++ /dev/null @@ -1,292 +0,0 @@ -import javascript - -abstract class FortifyTag extends JSDocTag { - FortifyTag() { - exists(JSDoc doc | - this = doc.getATagByTitle("SecSource") or - this = doc.getATagByTitle("SecSink") or - this = doc.getATagByTitle("SecValidate") or - this = doc.getATagByTitle("SecPassthrough") - ) - } - - string getFortifySpec() { - result = this.getDescription().splitAt("\n", 0).regexpCapture(".*(\\{.+\\}).*", 1) - } - - string getFortifySpecContents() { result = this.getFortifySpec().regexpCapture("\\{(.+)\\}", 1) } - - DocumentedObject getDocumentedObject() { result.getDocumentation() = this.getJSDocComment() } - - abstract string getYamlRow(); -} - -class FortifySourceTag extends FortifyTag { - FortifySourceTag() { exists(JSDoc doc | this = doc.getATagByTitle("SecSource")) } - - string getOutSpec() { - if this.getFortifySpecContents().splitAt("|", 0) = "return" - then result = "ReturnValue" - else result = "" - } - - string getFlags() { - if not exists(this.getFortifySpecContents().splitAt("|", 1)) - then result = "" - else result = this.getFortifySpecContents().splitAt("|", 1) - } - - predicate isJQuery() { this.getFile().getStem().prefix(6) = "jquery" } - - predicate hasNameTag() { exists(this.getJSDocComment().getATagByTitle("name")) } - - string getNameTag() { result = this.getJSDocComment().getATagByTitle("name").getName() } - - string getModuleName() { - /* "~/openui5/src/sap.ui.core/src/jquery.sap.sjax.js" */ - if this.isJQuery() or this.hasNameTag() - then result = "global" - else - /* "~/openui5/src/sap.ui.core/src/sap/base/util/UriParameters.js" */ - result = - this.getFile() - .getAbsolutePath() - .regexpCapture(".*(sap\\/[a-zA-Z]+\\/.*)", 1) - .regexpCapture("(.*)\\.js", 1) - } - - string getAPILangString() { - if this.isJQuery() - then - if this.hasNameTag() - then - result = - this.getNameTag().replaceAll("#", ".").regexpReplaceAll("([a-zA-Z]+)", "Member[$1]") + "." - + this.getOutSpec() - else - result = - this.getDocumentedObject().getExprString().regexpReplaceAll("([a-zA-Z]+)", "Member[$1]") + - "." + this.getOutSpec() - else - exists(DocumentedObject object | object = this.getDocumentedObject() | - if object.isNotAMethod() - then result = "Instance.Member[" + object.getExprString() + "]" - else result = "Instance.Member[" + object.getExprString() + "]" + "." + this.getOutSpec() - ) - } - - override string getYamlRow() { - result = - "[\"" + this.getModuleName() + "\", " + "\"" + this.getAPILangString() + "\", " + "\"" + - this.getFlags() + "\"" + "]" - } -} - -class FortifySinkTag extends FortifyTag { - FortifySinkTag() { exists(JSDoc doc | this = doc.getATagByTitle("SecSink")) } - - string getInSpec() { - if this.getFortifySpecContents().splitAt("|", 0).splitAt(" ").regexpMatch(".*\\*.*") - then result = this.getFortifySpecContents().splitAt("|", 0).splitAt(" ").replaceAll("*", "0..") - else - if count(this.getFortifySpecContents().splitAt("|", 0).splitAt(" ").toInt()) = 1 - then result = this.getFortifySpecContents().splitAt("|", 0).splitAt(" ") - else - result = - min(this.getFortifySpecContents().splitAt("|", 0).splitAt(" ").toInt()) + ".." + - max(this.getFortifySpecContents().splitAt("|", 0).splitAt(" ").toInt()) - } - - string getFlags() { - if not exists(this.getFortifySpecContents().splitAt("|", 1)) - then result = "" - else result = this.getFortifySpecContents().splitAt("|", 1) - } - - predicate hasNameTag() { exists(this.getJSDocComment().getATagByTitle("name")) } - - string getNameTag() { result = this.getJSDocComment().getATagByTitle("name").getName() } - - predicate isJQuery() { this.getFile().getStem().prefix(6) = "jquery" } - - string getModuleName() { - /* "~/openui5/src/sap.ui.core/src/jquery.sap.sjax.js" */ - if this.isJQuery() or this.hasNameTag() - then result = "global" - else - /* "~/openui5/src/sap.ui.core/src/sap/base/util/UriParameters.js" */ - result = - this.getFile() - .getAbsolutePath() - .regexpCapture(".*(sap\\/[a-zA-Z]+\\/.*)", 1) - .regexpCapture("(.*)\\.js", 1) - } - - string getAPILangString() { - if this.isJQuery() - then - if this.hasNameTag() - then - result = - this.getNameTag().replaceAll("#", ".").regexpReplaceAll("([a-zA-Z]+)", "Member[$1]") + - ".Argument[" + this.getInSpec() + "]" - else - result = - this.getDocumentedObject().getExprString().regexpReplaceAll("([a-zA-Z]+)", "Member[$1]") + - ".Argument[" + this.getInSpec() + "]" - else - exists(DocumentedObject object | object = this.getDocumentedObject() | - if object.isNotAMethod() - then result = "Instance.Member[" + object.getExprString() + "]" - else - result = - "Instance.Member[" + object.getExprString() + "].Argument[" + this.getInSpec() + "]" - ) - } - - override string getYamlRow() { - result = - "[\"" + this.getModuleName() + "\", " + "\"" + this.getAPILangString() + "\", " + "\"" + - this.getFlags() + "\"" + "]" - } -} - -class FortifyValidateTag extends FortifyTag { - FortifyValidateTag() { exists(JSDoc doc | this = doc.getATagByTitle("SecValidate")) } - - string getInSpec() { - result = this.getFortifySpecContents().splitAt("|", 0).splitAt(" ").replaceAll("*", "0..") - } - - string getOutSpec() { - if this.getFortifySpecContents().splitAt("|", 1) = "return" - then result = "ReturnValue" - else result = "" - } - - string getFlags() { - if not exists(this.getFortifySpecContents().splitAt("|", 2)) - then result = "" - else result = this.getFortifySpecContents().splitAt("|", 2) - } - - predicate hasNameTag() { exists(this.getJSDocComment().getATagByTitle("name")) } - - string getNameTag() { result = this.getJSDocComment().getATagByTitle("name").getName() } - - predicate isJQuery() { this.getFile().getStem().prefix(6) = "jquery" } - - string getModuleName() { - /* "~/openui5/src/sap.ui.core/src/jquery.sap.sjax.js" */ - if this.isJQuery() - then result = "global" - else - /* "~/openui5/src/sap.ui.core/src/sap/base/util/UriParameters.js" */ - result = - this.getFile() - .getAbsolutePath() - .regexpCapture(".*(sap/[a-zA-Z]+/.*)", 1) - .regexpCapture("(.*)\\.js", 1) - } - - string getPathString() { - if this.isJQuery() - then - result = - this.getDocumentedObject().getExprString().regexpReplaceAll("([a-zA-Z]+)", "Member[$1]") - else result = "" - } - - string getAPILangStringInSpec() { result = "Argument[" + this.getInSpec() + "]" } - - string getAPILangStringOutSpec() { result = this.getOutSpec() } - - override string getYamlRow() { - result = - "[\"" + this.getModuleName() + "\", " + "\"" + this.getPathString() + "\", " + "\"" + - this.getAPILangStringInSpec() + "\", " + "\"" + this.getAPILangStringOutSpec() + "\", " + - "\"taint\"]" - } -} - -class FortifyPassthroughTag extends FortifyTag { - FortifyPassthroughTag() { exists(JSDoc doc | this = doc.getATagByTitle("SecPassthrough")) } - - string getInSpec() { - result = this.getFortifySpecContents().splitAt("|", 0).splitAt(" ").replaceAll("*", "0..") - } - - string getOutSpec() { - if this.getFortifySpecContents().splitAt("|", 1) = "return" - then result = "ReturnValue" - else result = "" - } - - string getFlags() { - if not exists(this.getFortifySpecContents().splitAt("|", 2)) - then result = "" - else result = this.getFortifySpecContents().splitAt("|", 2) - } - - predicate isJQuery() { this.getFile().getStem().prefix(6) = "jquery" } - - string getModuleName() { - /* "~/openui5/src/sap.ui.core/src/jquery.sap.sjax.js" */ - if this.isJQuery() - then result = "global" - else - /* "~/openui5/src/sap.ui.core/src/sap/base/util/UriParameters.js" */ - result = - this.getFile() - .getAbsolutePath() - .regexpCapture(".*(sap/[a-zA-Z]+/.*)", 1) - .regexpCapture("(.*)\\.js", 1) - } - - string getPathString() { - if this.isJQuery() - then - result = - this.getDocumentedObject().getExprString().regexpReplaceAll("([a-zA-Z]+)", "Member[$1]") - else result = "" - } - - string getAPILangStringInSpec() { result = "Argument[" + this.getInSpec() + "]" } - - string getAPILangStringOutSpec() { result = this.getOutSpec() } - - override string getYamlRow() { - result = - "[\"" + this.getModuleName() + "\", " + "\"" + this.getPathString() + "\", " + "\"" + - this.getAPILangStringInSpec() + "\", " + "\"" + this.getAPILangStringOutSpec() + "\", " + - "\"taint\"]" - } -} - -class DocumentedObject extends Expr { - DocumentedObject() { - this instanceof AssignExpr - or - this instanceof VarDecl - or - this instanceof ObjectExpr and this.getParent() instanceof Property - } - - string getExprString() { - this instanceof AssignExpr and - exists(Expr lhs | lhs = this.(AssignExpr).getLhs() | - ( - if lhs.(DotExpr).getBase().toString() = "jQuery.sap" - then result = lhs.(DotExpr).getQualifiedName() - else result = lhs.(DotExpr).getPropertyName() - ) - ) - or - this instanceof ObjectExpr and - result = this.getParent().(Property).getAChildExpr().(Label).getName() - or - this instanceof VarDecl and result = this.(VarDecl).getName() - } - - predicate isNotAMethod() { this instanceof ObjectExpr } -} diff --git a/scripts/compile-cds.sh b/scripts/compile-cds.sh new file mode 100755 index 00000000..6f30a249 --- /dev/null +++ b/scripts/compile-cds.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# To use this script with the CodeQL CLI: +# 1. Set the `codeql database create` `--search-path`` argument to the `extractors/` directory in this repository, e.g.: +# ``` +# codeql database create --language javascript --search-path path/to/this/repo/extractors/ ... +# ``` +# 2. Run this script as an additional build command, for example by providing the +# `--command path/to/this/compile-cds.sh` argument to `codeql database create`. +# +# To use this script with the GitHub Actions workflow: +# 1. Set the `CODEQL_ACTION_EXTRA_OPTIONS` env var when running the `github/codeql-action/init` action +# to add the `--search-path` option as above. +# ``` +# - name: Initialize CodeQL +# uses: github/codeql-action/init@v3 +# env: +# CODEQL_ACTION_EXTRA_OPTIONS: '{"database":{"init":["--search-path","${{ github.workspace }}/extractors"]}}' +# .... +# ``` +# 2. Run the script as a command before the `github/codeql-action/analyze` step, e.g: +# ``` +# - name: Run CDS extractor +# run: | +# path/to/this/compile-cds.sh` + +set -eu + +# NOTE: the code below is copied in three places: +# - scripts/compile-cds.sh (here) +# - extractors/cds/tools/autobuild.sh +# - extractors/javascript/tools/pre-finalize.sh +# Any changes should be synchronized between these three places. + +# Do not extract CDS files if the CODEQL_EXTRACTOR_CDS_SKIP_EXTRACTION environment variable is set +if [ -z "${CODEQL_EXTRACTOR_CDS_SKIP_EXTRACTION:-}" ]; then + # Call the index-files command with the CDS extractor + "$CODEQL_DIST/codeql" database index-files \ + --language cds \ + --total-size-limit 10m \ + --include-extension=.cds \ + --prune **/node_modules/**/* \ + --prune **/.eslint/**/* \ + "$CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE" +fi \ No newline at end of file diff --git a/scripts/qlpack.yml b/scripts/qlpack.yml deleted file mode 100644 index f91b03b8..00000000 --- a/scripts/qlpack.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -library: false -warnOnImplicitThis: false -name: advanced-security/jsdoc-extraction -version: 0.0.1 -dependencies: - codeql/javascript-all: "^2.0.0"