Compare commits

...

54 Commits

Author SHA1 Message Date
jcreedcmu
728b8ca0fd Merge pull request #415 from jcreedcmu/jcreed/v1.2.1
Some checks failed
Code Scanning - CodeQL / codeql (push) Has been cancelled
Build Extension / Build (ubuntu-latest) (push) Has been cancelled
Build Extension / Build (windows-latest) (push) Has been cancelled
Build Extension / Test (ubuntu-latest) (push) Has been cancelled
Build Extension / Test (windows-latest) (push) Has been cancelled
Release / Release (push) Has been cancelled
Update CHANGELOG for release
2020-05-29 13:34:04 -04:00
Jason Reed
edd5734de8 Update CHANGELOG for release 2020-05-29 13:32:29 -04:00
jcreedcmu
88a4cc528e Merge pull request #414 from aeisenberg/aeisenberg/lgtm-explore
Update link to lgtm to go to the explore section
2020-05-29 13:31:59 -04:00
Andrew Eisenberg
a732f19a3d Update link to lgtm to go to the explore section
Makes it easier for new users to search for projects.
2020-05-29 10:29:04 -07:00
jcreedcmu
18c9333f37 Merge pull request #413 from aeisenberg/aeisenberg/rush
Add node v14 to allowed node versions
2020-05-29 13:21:52 -04:00
Andrew Eisenberg
010000b878 Add node v14 to allowed node versions 2020-05-29 10:20:13 -07:00
jcreedcmu
7b5f7499b4 Merge pull request #411 from aeisenberg/aeisenberg/readme-lgtm
Update the download from LGTM section in the readme
2020-05-29 11:32:41 -04:00
Andrew Eisenberg
292bec2ea5 Update extensions/ql-vscode/README.md
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
2020-05-29 08:18:17 -07:00
Andrew Eisenberg
910a877d06 Update extensions/ql-vscode/README.md
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
2020-05-29 08:18:11 -07:00
Andrew Eisenberg
80023f1304 Update extensions/ql-vscode/README.md
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
2020-05-29 08:18:04 -07:00
Andrew Eisenberg
8e8247e986 Update extensions/ql-vscode/README.md
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
2020-05-29 08:17:57 -07:00
Andrew Eisenberg
d92e0b5568 Update extensions/ql-vscode/README.md
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
2020-05-29 08:17:50 -07:00
Andrew Eisenberg
d3c1e7688e Update extensions/ql-vscode/README.md
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
2020-05-29 08:17:42 -07:00
Andrew Eisenberg
3e9c58869c Update extensions/ql-vscode/README.md
Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com>
2020-05-29 08:17:34 -07:00
Andrew Eisenberg
c0a8c7affd Update the download from LGTM section in the readme 2020-05-28 13:12:21 -07:00
Andrew Eisenberg
f2575e4d4a Better error handling for downloading dbs at invalid URLs
We do our best to extract a readable error message from the
response.
2020-05-28 11:53:41 -07:00
Andrew Eisenberg
87315b8f33 Update changelog 2020-05-28 11:53:41 -07:00
Andrew Eisenberg
a338683a71 Add unit tests for databaseFetcher 2020-05-28 11:53:41 -07:00
Andrew Eisenberg
a541b11a37 Add more flexibility with Uri parsing for LGTM
Ensure that providers other than `g` are accepted and that subpages
are ignored.
2020-05-28 11:53:41 -07:00
Andrew Eisenberg
e2771a8922 Remove code scanning on pull request 2020-05-28 11:53:41 -07:00
Andrew Eisenberg
16e09b7ae9 Add better error handling
* ensure error appears when an invalid URL is entered
* ensure error messages are understandable by users
2020-05-28 11:53:41 -07:00
Andrew Eisenberg
1c1dbc95c7 Allow download from lgtm
Adds a new command to download databases from lgtm. It's working,
though need to create better error messages on failure.
2020-05-28 11:53:41 -07:00
jcreedcmu
dd9fafc27c Merge pull request #407 from jcreedcmu/jcreed/view-sarif
Allow viewing SARIF from query history view
2020-05-28 08:08:56 -04:00
jcreedcmu
7172505e25 Merge pull request #328 from jcreedcmu/jcreed/restart-on-segfault
Retry integration tests only on segfault
2020-05-27 14:10:18 -04:00
Jason Reed
7b99bdfc88 Address review comments. 2020-05-27 14:08:03 -04:00
Jason Reed
bb16454ab7 Only show 'view SARIF' if SARIF exists. 2020-05-27 11:57:31 -04:00
Jason Reed
70529a81f3 Add "View SARIF" command to query history context menu. 2020-05-27 11:30:50 -04:00
Jason Reed
7db6bc8228 Remove dead code. 2020-05-27 10:59:32 -04:00
Jason Reed
41fab207dc Retry integration tests only on segfault 2020-05-27 10:52:31 -04:00
Jason Reed
a8bad9ecb8 Upgrade vscode-test dependency to 1.4.0 2020-05-27 10:52:31 -04:00
jcreedcmu
17901bee0c Merge pull request #406 from shati-patel/qldoc
Update capitalization of "QLDoc" in CHANGELOG.md
2020-05-27 10:34:36 -04:00
Shati Patel
e7d041af68 Update CHANGELOG.md 2020-05-27 10:09:22 +01:00
jcreedcmu
9afd676c1e Merge pull request #403 from aeisenberg/aeisenberg/codeql-action
Introduce codeql code scanning action
2020-05-26 17:05:04 -04:00
Andrew Eisenberg
7bf719f632 Introduce codeql code scanning action 2020-05-26 14:00:28 -07:00
Andrew Eisenberg
32622b1b9f Update changelog 2020-05-26 12:01:22 -07:00
Andrew Eisenberg
8262ecf990 Fix syntax error in ql library 2020-05-26 12:01:22 -07:00
Andrew Eisenberg
0817abd6ac Use Uri.file instead of Uri.parse 2020-05-26 12:01:22 -07:00
Andrew Eisenberg
821ec9b8f7 Add tests for database uri fixing 2020-05-26 12:01:22 -07:00
Andrew Eisenberg
b0328b03a0 Allow users more flexibility when opening a DB
Closes #383.

See the heuristics in the issue.
2020-05-26 12:01:22 -07:00
Andrew Eisenberg
2d7d6fb873 Update changelog 2020-05-26 11:39:57 -07:00
Andrew Eisenberg
b7201c04dc Add onEnterRules for language config
This change provides proper indent/outdent for block comments. Through
onEnterRules. Because onEnterRules are not exactly API, I had to use
a back door to implement them.

Also, it tweaks the language-configuration.json by adding more support
for things like word boundaries and auto-closing pairs.

Since QL has similar syntactical items as JavaScriot, I started with
the JS lang config and removed single quotes and back ticks.
2020-05-26 11:39:57 -07:00
Andrew Eisenberg
8db488563b Add more tests for the archive-filesystem-provider 2020-05-26 10:50:03 -07:00
jcreedcmu
fac5f98d80 Merge pull request #382 from jcreedcmu/jcreed/zip-logging
Log more details when source archive entries aren't found
2020-05-26 12:38:40 -04:00
jcreedcmu
fccec96926 Merge pull request #389 from jcreedcmu/jcreed/no-defs-err
Add error message when there are no definitions/references queries available.
2020-05-26 12:38:24 -04:00
Jason Reed
8cadd3dcab Add error message when no definitions queries. 2020-05-21 14:45:55 -04:00
jcreedcmu
d9e1a6f82a Merge pull request #385 from github/jcreedcmu-patch-1
Update README.md
2020-05-21 11:06:15 -04:00
Bas van Schaik
f47a88dcb1 Update README.md 2020-05-21 16:02:31 +01:00
Bas van Schaik
8cab3e9c6f Update README.md 2020-05-21 16:02:02 +01:00
jcreedcmu
165f3957ed Update README.md
Include instructions about how to update libraries
2020-05-21 10:58:00 -04:00
Jason Reed
3e4eeeb8fd Log more details when source archive entries aren't found 2020-05-20 08:49:58 -04:00
jcreedcmu
038e0a3c63 Merge pull request #381 from dbartol/dbartol/publish
Avoid need for `build` directory
2020-05-20 07:51:58 -04:00
Dave Bartolomeo
3e7084f65d Remove build directory 2020-05-19 17:13:30 -04:00
Dave Bartolomeo
18bb4b0231 Avoid need for build directory 2020-05-19 17:03:56 -04:00
github-actions[bot]
8cb5661330 Bump version to v1.2.1 2020-05-19 12:48:50 -07:00
30 changed files with 874 additions and 1523 deletions

21
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: "Code Scanning - CodeQL"
on:
push:
schedule:
- cron: '0 0 * * 0'
jobs:
codeql:
strategy:
fail-fast: false
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -18,11 +18,12 @@ jobs:
with:
node-version: '10.18.1'
- name: Install dependencies
run: node common/scripts/install-run-rush.js install
shell: bash
- name: Build
run: |
cd build
npm install
npm run build-ci
run: node common/scripts/install-run-rush.js build
shell: bash
- name: Prepare artifacts
@@ -55,11 +56,12 @@ jobs:
node-version: '10.18.1'
# We have to build the dependencies in `lib` before running any tests.
- name: Install dependencies
run: node common/scripts/install-run-rush.js install
shell: bash
- name: Build
run: |
cd build
npm install
npm run build-ci
run: node common/scripts/install-run-rush.js build
shell: bash
- name: Lint

View File

@@ -33,12 +33,12 @@ jobs:
with:
node-version: '10.18.1'
- name: Install dependencies
run: node common/scripts/install-run-rush.js install
shell: bash
- name: Build
run: |
cd build
npm install
# Release build instead of dev build.
npm run build-release
run: node common/scripts/install-run-rush.js build --release
shell: bash
- name: Prepare artifacts

View File

@@ -1,12 +0,0 @@
GitHub Actions Build directory
===
The point of this directory is to allow us to do a local installation *of* the rush
tool, since
- installing globally is not permitted on github actions
- installing locally in the root directory of the repo creates `node_modules` there,
and rush itself gives error messages since it thinks `node_modules` is not supposed
to exist, since rush is supposed to be managing subproject dependencies.
Running rush from a subdirectory searches parent directories for `rush.json`
and does the build starting from that file's location.

1293
build/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +0,0 @@
{
"name": "build",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"@microsoft/rush": "^5.10.3"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rush update && rush build",
"build-ci": "rush install && rush build",
"build-release": "rush install && rush build --release"
},
"author": "GitHub"
}

View File

@@ -84,7 +84,7 @@ dependencies:
vsce: 1.74.0
vscode-jsonrpc: 5.0.1
vscode-languageclient: 6.1.3
vscode-test: 1.3.0
vscode-test: 1.4.0
vscode-test-adapter-api: 1.7.0
vscode-test-adapter-util: 0.7.0
webpack: 4.42.0_webpack@4.42.0
@@ -7437,7 +7437,7 @@ packages:
vscode: ^1.24.0
resolution:
integrity: sha512-eAsB8koXct5JytvUcV62wLEBCQfsoclauzMLEFT6H0qBr1h8LyRc+dGDcs48pO28yFOo6VV+5AwCRLxTKh7TzQ==
/vscode-test/1.3.0:
/vscode-test/1.4.0:
dependencies:
http-proxy-agent: 2.1.0
https-proxy-agent: 2.2.4
@@ -7446,7 +7446,7 @@ packages:
engines:
node: '>=8.9.3'
resolution:
integrity: sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw==
integrity: sha512-Jt7HNGvSE0+++Tvtq5wc4hiXLIr2OjDShz/gbAfM/mahQpy4rKBnmOK33D+MR67ATWviQhl+vpmU3p/qwSH/Pg==
/watchpack/1.6.0:
dependencies:
chokidar: 2.1.8
@@ -7836,7 +7836,7 @@ packages:
peerDependencies:
glob: '*'
resolution:
integrity: sha512-14DvfY6Fj3HXp2/CNJ2zNh9MA8zPw9mUcr8WqkSsYvJow7JMcIlJ//OOONwpoSWtfrk1bk6Cin7jj9H79ItHQQ==
integrity: sha512-NkoIMaJdASYX4NjcB+nsEk/8Ff/2RLvHwL0efNOny3no6aNuJ3EkpNK0ZdX7HQdmTdY3IJPmjoJ3Rn4pkbxgdA==
tarball: 'file:projects/build-tasks.tgz'
version: 0.0.0
'file:projects/semmle-bqrs.tgz_typescript@3.8.3':
@@ -7851,7 +7851,7 @@ packages:
peerDependencies:
typescript: '*'
resolution:
integrity: sha512-24GdnvMbGfQIWMfgDhift+kYJDnG7dX03NrpX4ajZ2rckteysvq2/K7XI1OXGvUuqrt3m0/+GRDHpSI9XKDJJA==
integrity: sha512-lE3FBYrOVF1JH0ZqvF4YA+bed3JPWYucsnFe+XL140a/YR19XD+TTHIfov7VpR9qdyWfARgvmR+gf2qsguXTKQ==
tarball: 'file:projects/semmle-bqrs.tgz'
version: 0.0.0
'file:projects/semmle-io-node.tgz_typescript@3.8.3':
@@ -7866,7 +7866,7 @@ packages:
peerDependencies:
typescript: '*'
resolution:
integrity: sha512-Bj0ax/bASrHV7tamOuXZZdd3UOB4NBKdjdszIRaDvDRTu8RlEst+TVoUhkfy30qb2/6ePp3/juOJyyiBJN7u8Q==
integrity: sha512-MD9edC5HjrCfPmhktw6XmWotUmperj27/hDZiuMbuSlJ4jRKyiBtJ8Vk2Y4U41TrzsBlJfAwZW8tetPw5ujiLg==
tarball: 'file:projects/semmle-io-node.tgz'
version: 0.0.0
'file:projects/semmle-io.tgz_typescript@3.8.3':
@@ -7880,7 +7880,7 @@ packages:
peerDependencies:
typescript: '*'
resolution:
integrity: sha512-NtyviDSevxbd+hj4J66LucOzo8LU2hJ1Jh0eHw0Qu3tRZPUT8HcQlseyy29AvZR8n8eppfEZiAm/JdiHfmRPMA==
integrity: sha512-ta1lLi1COIeFwpwH523cWheWx6OE8GTqguQmOA7G6CwRF41RYbbREf/4KlOLKO/uG2akhhl+3gcWY2c5/VDC/A==
tarball: 'file:projects/semmle-io.tgz'
version: 0.0.0
'file:projects/semmle-vscode-utils.tgz':
@@ -7892,14 +7892,14 @@ packages:
dev: false
name: '@rush-temp/semmle-vscode-utils'
resolution:
integrity: sha512-5y5r8SDoN9Fp44naC9gUe8rOexeckXg2T0h9QCJAIcEgnFqOxzRc6Rv9gbMUStFKNh+rFlvmYmgPAdg5QkfgUg==
integrity: sha512-Dbwt0/Wd0VNKkRZRjFQv3hmGy/UDt36HDtEDsNgZIcQACoY1j2+mJavpQ+ZzCg4Ftj06eHDVk+ptzUEd+8Ybzw==
tarball: 'file:projects/semmle-vscode-utils.tgz'
version: 0.0.0
'file:projects/typescript-config.tgz':
dev: false
name: '@rush-temp/typescript-config'
resolution:
integrity: sha512-XuUIySaNoooIduvehnlKYaHqZJmmQoCqB1RtKhNszjCYZaSSJAnKVucViWBf5oNLKSNP7NchrD7gcoBlQ3xYvw==
integrity: sha512-qJbtY2jvt6LKkmUt/seiYyXSEB6Oip/rW+SxofQEnpyplgIQv7whTZb6g5pwlSLGl8goTaQFm4NfazKhFmxXvQ==
tarball: 'file:projects/typescript-config.tgz'
version: 0.0.0
'file:projects/vscode-codeql.tgz':
@@ -7970,7 +7970,7 @@ packages:
vsce: 1.74.0
vscode-jsonrpc: 5.0.1
vscode-languageclient: 6.1.3
vscode-test: 1.3.0
vscode-test: 1.4.0
vscode-test-adapter-api: 1.7.0
vscode-test-adapter-util: 0.7.0
webpack: 4.42.0_webpack@4.42.0
@@ -7978,7 +7978,7 @@ packages:
dev: false
name: '@rush-temp/vscode-codeql'
resolution:
integrity: sha512-YwJoYdN8GMZlZHiLXhC1jw2BfrBJOpoCDtKQ78HphTslH7S94cUbASmZCgXKPkb9aIijsOY3JHE4/Od6lqB65w==
integrity: sha512-ClyrIRqnMYMmVHtHvW8MvS4GrRSt/dXY3lxBpxSv3wSJ67pEvWKea+DJyeVN2zaHz1/7gAOWQHhwBz6O3lEq6w==
tarball: 'file:projects/vscode-codeql.tgz'
version: 0.0.0
registry: ''
@@ -8068,7 +8068,7 @@ specifiers:
vsce: ^1.65.0
vscode-jsonrpc: ^5.0.1
vscode-languageclient: ^6.1.3
vscode-test: ^1.0.0
vscode-test: ^1.4.0
vscode-test-adapter-api: ~1.7.0
vscode-test-adapter-util: ~0.7.0
webpack: ^4.38.0

View File

@@ -1,5 +1,12 @@
# CodeQL for Visual Studio Code: Changelog
## 1.2.1 - 29 May 2020
- Better formatting and autoindentation when adding QLDoc comments to `.ql` and `.qll` files.
- Allow for more flexibility when opening a database in the workspace. A user can now choose the actual database folder, or the nested `db-*` folder.
- Add query history menu command for viewing corresponding SARIF file.
- Add ability for users to download databases directly from LGTM.com.
## 1.2.0 - 19 May 2020
- Enable 'Go to Definition' and 'Go to References' on source archive

View File

@@ -2,10 +2,10 @@
This project is an extension for Visual Studio Code that adds rich language support for [CodeQL](https://help.semmle.com/codeql) and allows you to easily find problems in codebases. In particular, the extension:
* Enables you to use CodeQL to query databases generated from source code.
* Shows the flow of data through the results of path queries, which is essential for triaging security results.
* Provides an easy way to run queries from the large, open source repository of [CodeQL security queries](https://github.com/Semmle/ql).
* Adds IntelliSense to support you writing and editing your own CodeQL query and library files.
- Enables you to use CodeQL to query databases generated from source code.
- Shows the flow of data through the results of path queries, which is essential for triaging security results.
- Provides an easy way to run queries from the large, open source repository of [CodeQL security queries](https://github.com/github/codeql).
- Adds IntelliSense to support you writing and editing your own CodeQL query and library files.
To see what has changed in the last few versions of the extension, see the [Changelog](https://github.com/github/vscode-codeql/blob/master/extensions/ql-vscode/CHANGELOG.md).
@@ -14,18 +14,18 @@ To see what has changed in the last few versions of the extension, see the [Chan
The information in this `README` file describes the quickest way to start using CodeQL.
For information about other configurations, see the separate [CodeQL help](https://help.semmle.com/codeql/codeql-for-vscode.html).
**Quick start: Installing and configuring the extension**
### Quick start: Installing and configuring the extension
1. [Install the extension](#installing-the-extension).
1. [Check access to the CodeQL CLI](#checking-access-to-the-codeql-cli).
1. [Clone the CodeQL starter workspace](#cloning-the-codeql-starter-workspace).
**Quick start: Using CodeQL**
### Quick start: Using CodeQL
1. [Import a database from LGTM](#importing-a-database-from-lgtm).
1. [Run a query](#running-a-query).
-----
---
## Quick start: Installing and configuring the extension
@@ -49,11 +49,26 @@ If you have any difficulty with CodeQL CLI access, see the **CodeQL Extension Lo
### Cloning the CodeQL starter workspace
When you're working with CodeQL, you need access to the standard CodeQL libraries and queries.
Initially, we recommend that you clone and use the ready-to-use starter workspace, https://github.com/github/vscode-codeql-starter/.
Initially, we recommend that you clone and use the ready-to-use [starter workspace](https://github.com/github/vscode-codeql-starter/).
This includes libraries and queries for the main supported languages, with folders set up ready for your custom queries. After cloning the workspace (use `git clone --recursive`), you can use it in the same way as any other VS Code workspace—with the added advantage that you can easily update the CodeQL libraries.
For information about configuring an existing workspace for CodeQL, [see the documentation](https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html#updating-an-existing-workspace-for-codeql).
## Upgrading CodeQL standard libraries
You can easily keep up-to-date with the latest changes to the [CodeQL standard libraries](https://github.com/github/codeql).
If you're using the [CodeQL starter workspace](https://github.com/github/vscode-codeql-starter/), you can pull in the latest standard libraries by running:
```shell
git pull
git submodule update --recursive
```
in the starter workspace directory.
If you're using your own clone of the CodeQL standard libraries, you can do a `git pull` from where you have the libraries checked out.
## Quick start: Using CodeQL
You can find all the commands contributed by the extension in the Command Palette (**Ctrl+Shift+P** or **Cmd+Shift+P**) by typing `CodeQL`, many of them are also accessible through the interface, and via keyboard shortcuts.
@@ -62,15 +77,13 @@ You can find all the commands contributed by the extension in the Command Palett
While you can use the [CodeQL CLI to create your own databases](https://help.semmle.com/codeql/codeql-cli/procedures/create-codeql-database.html), the simplest way to start is by downloading a database from LGTM.com.
1. Log in to LGTM.com.
1. Find a project you're interested in and display the **Integrations** tab (for example, [Apache Kafka](https://lgtm.com/projects/g/apache/kafka/ci/)).
1. Scroll to the **CodeQL databases for local analysis** section at the bottom of the page.
1. Download databases for the languages that you want to explore.
1. For each database that you want to import:
1. Go to the CodeQL Databases view in the sidebar. Hover over the Databases title bar and click the icon to **Choose Database from Archive**.
1. Browse to the zipped database that you downloaded from LGTM.
Once you've added a CodeQL database, it is displayed in the Databases view.
1. Open [LGTM.com](https://lgtm.com/#explore) in your browser.
1. Search for a project you're interested in, for example [Apache Kafka](https://lgtm.com/projects/g/apache/kafka).
1. Copy the link to that project, for example `https://lgtm.com/projects/g/apache/kafka`.
1. In VS Code, open the Command Palette and choose the **CodeQL: Download Database from LGTM** command.
1. Paste the link you copied earlier.
1. Select the language for the database you want to download (only required if the project has databases for multiple languages).
1. Once the CodeQL database has been imported, it is displayed in the Databases view.
### Running a query
@@ -78,7 +91,7 @@ The instructions below assume that you're using the CodeQL starter workspace, or
1. Expand the `ql` folder and locate a query to run. The standard queries are grouped by target language and then type, for example: `ql/java/ql/src/Likely Bugs`.
1. Open a query (`.ql`) file.
3. Right-click in the query window and select **CodeQL: Run Query**. Alternatively, open the Command Palette (**Ctrl+Shift+P** or **Cmd+Shift+P**), type `Run Query`, then select **CodeQL: Run Query**.
1. Right-click in the query window and select **CodeQL: Run Query**. Alternatively, open the Command Palette (**Ctrl+Shift+P** or **Cmd+Shift+P**), type `Run Query`, then select **CodeQL: Run Query**.
The CodeQL extension runs the query on the current database using the CLI and reports progress in the bottom right corner of the application.
When the results are ready, they're displayed in the CodeQL Query Results view. Use the dropdown menu to choose between different forms of result output.
@@ -89,10 +102,10 @@ If there are any problems running a query, a notification is displayed in the bo
For more information about the CodeQL extension, [see the documentation](https://help.semmle.com/codeql/codeql-for-vscode.html). Otherwise, you could:
* [Create a database for a different codebase](https://help.semmle.com/codeql/codeql-cli/procedures/create-codeql-database.html).
* [Try out variant analysis](https://help.semmle.com/QL/learn-ql/ql-training.html).
* [Learn more about CodeQL](https://help.semmle.com/QL/learn-ql/).
* [Read how security researchers use CodeQL to find CVEs](https://securitylab.github.com/research).
- [Create a database for a different codebase](https://help.semmle.com/codeql/codeql-cli/procedures/create-codeql-database.html).
- [Try out variant analysis](https://help.semmle.com/QL/learn-ql/ql-training.html).
- [Learn more about CodeQL](https://help.semmle.com/QL/learn-ql/).
- [Read how security researchers use CodeQL to find CVEs](https://securitylab.github.com/research).
## License

View File

@@ -1,72 +1,38 @@
{
"comments": {
// symbol used for single line comment. Remove this entry if your language does not support line comments
"lineComment": "//",
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
"blockComment": [
"/*",
"*/"
]
},
// symbols used as brackets
"brackets": [
[
"{",
"}"
],
[
"[",
"]"
],
[
"(",
")"
]
],
// symbols that are auto closed when typing
"autoClosingPairs": [
[
"{",
"}"
],
[
"[",
"]"
],
[
"(",
")"
],
[
"\"",
"\""
],
[
"'",
"'"
]
],
// symbols that that can be used to surround a selection
"surroundingPairs": [
[
"{",
"}"
],
[
"[",
"]"
],
[
"(",
")"
],
[
"\"",
"\""
],
[
"'",
"'"
]
]
}
"comments": {
"lineComment": "//",
"blockComment": ["/*", "*/"]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
{ "open": "{", "close": "}" },
{ "open": "[", "close": "]" },
{ "open": "(", "close": ")" },
{ "open": "'", "close": "'", "notIn": ["string", "comment"] },
{ "open": "\"", "close": "\"", "notIn": ["string"] },
{ "open": "/**", "close": " */", "notIn": ["string"] }
],
"autoCloseBefore": ";:.=}])> \n\t",
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["'", "'"],
["\"", "\""]
],
"folding": {
"markers": {
"start": "^\\s*//\\s*#?region\\b",
"end": "^\\s*//\\s*#?endregion\\b"
}
},
"wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\.\\<\\>\\/\\?\\s]+)",
"indentationRules": {
"increaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\}\\]].*$",
"decreaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"']*|\\([^)\"']*|\\[[^\\]\"']*)$"
}
}

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M16.010 6.49c-3.885 0-7.167 0.906-9.328 2.813-0.063-0.12-0.109-0.219-0.188-0.339-0.224-0.365-0.438-0.776-1.104-1.188-0.411-0.26-0.87-0.438-1.349-0.516-0.208-0.021-0.422-0.021-0.63 0l0.135-0.016c-1.214 0-1.922 0.724-2.385 1.354-0.458 0.625-0.755 1.328-0.948 2.099-0.38 1.542-0.385 3.536 1.083 5.026 0.766 0.781 1.667 1.151 2.484 1.37 0.156 0.042 0.297 0.052 0.448 0.083 0.531 2.521 2.104 4.656 4.208 5.839v0.005c1.24 0.693 2.417 1.010 3.297 1.349 1.234 0.479 2.536 1 4.052 1.135l0.078 0.005h0.198c1.745 0 3.063-0.703 4.203-1.141 0.875-0.333 2.052-0.641 3.302-1.344 0.578-0.323 1.115-0.719 1.594-1.172 1.318-1.234 2.229-2.839 2.625-4.599 1.115-0.182 2.141-0.719 2.922-1.536 1.464-1.484 1.458-3.479 1.078-5.021-0.193-0.771-0.49-1.474-0.948-2.099-0.458-0.63-1.172-1.354-2.385-1.354l0.135 0.016c-0.208-0.021-0.422-0.021-0.63 0-0.479 0.078-0.938 0.255-1.344 0.516-0.667 0.411-0.88 0.823-1.104 1.182-0.073 0.12-0.12 0.219-0.188 0.333-2.156-1.901-5.432-2.802-9.313-2.802zM16.042 8.313c4.745 0 8.016 1.422 9.411 3.964 0.839-0.323 1.453-2.521 2.146-2.948 0.563-0.344 0.885-0.26 0.885-0.26 1.271 0 2.578 3.729 0.953 5.38-0.859 0.875-2.443 1.12-3.229 1.057-0.063 2.542-1.542 4.833-3.5 5.932-1 0.563-2.068 0.854-3.063 1.234-1.229 0.469-2.38 1.016-3.547 1.016h-0.125c-1.161-0.099-2.318-0.542-3.547-1.016-0.995-0.38-2.068-0.682-3.063-1.24-1.948-1.099-3.427-3.391-3.49-5.927-0.781 0.068-2.385-0.177-3.245-1.057-1.625-1.651-0.318-5.38 0.948-5.38 0 0 0.328-0.083 0.885 0.26 0.698 0.427 1.318 2.646 2.161 2.953 1.391-2.547 4.667-3.969 9.417-3.969zM10.875 11.422c-2.276-0.042-4.146 1.792-4.146 4.068 0 2.281 1.87 4.115 4.146 4.073 5.328-0.099 5.328-8.047 0-8.141zM21.208 11.422c-5.427 0-5.427 8.141 0 8.141s5.427-8.141 0-8.141zM11.453 13.708c2.349 0.063 2.349 3.552 0 3.615-1.182 0-2.042-1.115-1.75-2.255 0.318 0.771 1.469 0.547 1.464-0.292 0-0.406-0.318-0.745-0.729-0.76 0.302-0.203 0.656-0.313 1.016-0.307zM20.641 13.708c2.344 0.063 2.344 3.552 0 3.615-1.182 0-2.047-1.115-1.755-2.255 0.229 0.552 0.979 0.641 1.328 0.146 0.344-0.49 0.010-1.167-0.589-1.193 0.297-0.208 0.651-0.313 1.016-0.313zM15.359 19.906c-0.318 0.026-0.5 0.193-0.5 0.635 0 0.281 0.182 0.484 0.5 0.484 0.229 0 0.266-0.323 0.047-0.375-0.031-0.005-0.172-0.057-0.172-0.182 0-0.12 0-0.167 0.24-0.198 0.104-0.016 0.156-0.141 0.125-0.24s-0.125-0.135-0.24-0.125zM16.724 19.906c-0.115-0.005-0.208 0.026-0.24 0.125s0.021 0.224 0.125 0.24c0.24 0.031 0.24 0.078 0.24 0.198 0 0.125-0.141 0.177-0.172 0.182-0.219 0.052-0.182 0.375 0.042 0.375 0.323 0 0.51-0.203 0.51-0.484 0-0.443-0.188-0.609-0.505-0.635z" fill="#C5C5C5"/>
<line y2="24" x2="16" y1="26" x1="32" stroke-width="2" stroke="green" fill="none"/>
<line y2="16" x2="24" y1="32" x1="24" stroke-width="1" stroke="green" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M16.010 6.49c-3.885 0-7.167 0.906-9.328 2.813-0.063-0.12-0.109-0.219-0.188-0.339-0.224-0.365-0.438-0.776-1.104-1.188-0.411-0.26-0.87-0.438-1.349-0.516-0.208-0.021-0.422-0.021-0.63 0l0.135-0.016c-1.214 0-1.922 0.724-2.385 1.354-0.458 0.625-0.755 1.328-0.948 2.099-0.38 1.542-0.385 3.536 1.083 5.026 0.766 0.781 1.667 1.151 2.484 1.37 0.156 0.042 0.297 0.052 0.448 0.083 0.531 2.521 2.104 4.656 4.208 5.839v0.005c1.24 0.693 2.417 1.010 3.297 1.349 1.234 0.479 2.536 1 4.052 1.135l0.078 0.005h0.198c1.745 0 3.063-0.703 4.203-1.141 0.875-0.333 2.052-0.641 3.302-1.344 0.578-0.323 1.115-0.719 1.594-1.172 1.318-1.234 2.229-2.839 2.625-4.599 1.115-0.182 2.141-0.719 2.922-1.536 1.464-1.484 1.458-3.479 1.078-5.021-0.193-0.771-0.49-1.474-0.948-2.099-0.458-0.63-1.172-1.354-2.385-1.354l0.135 0.016c-0.208-0.021-0.422-0.021-0.63 0-0.479 0.078-0.938 0.255-1.344 0.516-0.667 0.411-0.88 0.823-1.104 1.182-0.073 0.12-0.12 0.219-0.188 0.333-2.156-1.901-5.432-2.802-9.313-2.802zM16.042 8.313c4.745 0 8.016 1.422 9.411 3.964 0.839-0.323 1.453-2.521 2.146-2.948 0.563-0.344 0.885-0.26 0.885-0.26 1.271 0 2.578 3.729 0.953 5.38-0.859 0.875-2.443 1.12-3.229 1.057-0.063 2.542-1.542 4.833-3.5 5.932-1 0.563-2.068 0.854-3.063 1.234-1.229 0.469-2.38 1.016-3.547 1.016h-0.125c-1.161-0.099-2.318-0.542-3.547-1.016-0.995-0.38-2.068-0.682-3.063-1.24-1.948-1.099-3.427-3.391-3.49-5.927-0.781 0.068-2.385-0.177-3.245-1.057-1.625-1.651-0.318-5.38 0.948-5.38 0 0 0.328-0.083 0.885 0.26 0.698 0.427 1.318 2.646 2.161 2.953 1.391-2.547 4.667-3.969 9.417-3.969zM10.875 11.422c-2.276-0.042-4.146 1.792-4.146 4.068 0 2.281 1.87 4.115 4.146 4.073 5.328-0.099 5.328-8.047 0-8.141zM21.208 11.422c-5.427 0-5.427 8.141 0 8.141s5.427-8.141 0-8.141zM11.453 13.708c2.349 0.063 2.349 3.552 0 3.615-1.182 0-2.042-1.115-1.75-2.255 0.318 0.771 1.469 0.547 1.464-0.292 0-0.406-0.318-0.745-0.729-0.76 0.302-0.203 0.656-0.313 1.016-0.307zM20.641 13.708c2.344 0.063 2.344 3.552 0 3.615-1.182 0-2.047-1.115-1.755-2.255 0.229 0.552 0.979 0.641 1.328 0.146 0.344-0.49 0.010-1.167-0.589-1.193 0.297-0.208 0.651-0.313 1.016-0.313zM15.359 19.906c-0.318 0.026-0.5 0.193-0.5 0.635 0 0.281 0.182 0.484 0.5 0.484 0.229 0 0.266-0.323 0.047-0.375-0.031-0.005-0.172-0.057-0.172-0.182 0-0.12 0-0.167 0.24-0.198 0.104-0.016 0.156-0.141 0.125-0.24s-0.125-0.135-0.24-0.125zM16.724 19.906c-0.115-0.005-0.208 0.026-0.24 0.125s0.021 0.224 0.125 0.24c0.24 0.031 0.24 0.078 0.24 0.198 0 0.125-0.141 0.177-0.172 0.182-0.219 0.052-0.182 0.375 0.042 0.375 0.323 0 0.51-0.203 0.51-0.484 0-0.443-0.188-0.609-0.505-0.635z" fill="#424242"/>
<line y2="24" x2="16" y1="26" x1="32" stroke-width="2" stroke="green" fill="none"/>
<line y2="16" x2="24" y1="32" x1="24" stroke-width="1" stroke="green" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -4,7 +4,7 @@
"description": "CodeQL for Visual Studio Code",
"author": "GitHub",
"private": true,
"version": "1.2.0",
"version": "1.2.1",
"publisher": "GitHub",
"license": "MIT",
"icon": "media/VS-marketplace-CodeQL-icon.png",
@@ -30,10 +30,12 @@
"onCommand:codeQLDatabases.chooseDatabaseFolder",
"onCommand:codeQLDatabases.chooseDatabaseArchive",
"onCommand:codeQLDatabases.chooseDatabaseInternet",
"onCommand:codeQLDatabases.chooseDatabaseLgtm",
"onCommand:codeQL.setCurrentDatabase",
"onCommand:codeQL.chooseDatabaseFolder",
"onCommand:codeQL.chooseDatabaseArchive",
"onCommand:codeQL.chooseDatabaseInternet",
"onCommand:codeQL.chooseDatabaseLgtm",
"onCommand:codeQLDatabases.chooseDatabase",
"onCommand:codeQLDatabases.setCurrentDatabase",
"onCommand:codeQL.quickQuery",
@@ -200,6 +202,14 @@
"dark": "media/dark/cloud-download.svg"
}
},
{
"command": "codeQLDatabases.chooseDatabaseLgtm",
"title": "Download from LGTM",
"icon": {
"light": "media/light/lgtm-plus.svg",
"dark": "media/dark/lgtm-plus.svg"
}
},
{
"command": "codeQL.setCurrentDatabase",
"title": "CodeQL: Set Current Database"
@@ -244,6 +254,10 @@
"command": "codeQL.chooseDatabaseInternet",
"title": "CodeQL: Download Database"
},
{
"command": "codeQL.chooseDatabaseLgtm",
"title": "CodeQL: Download Database from LGTM"
},
{
"command": "codeQLDatabases.sortByName",
"title": "Sort by Name",
@@ -284,6 +298,10 @@
"command": "codeQLQueryHistory.showQueryText",
"title": "Show Query Text"
},
{
"command": "codeQLQueryHistory.viewSarif",
"title": "View SARIF"
},
{
"command": "codeQLQueryResults.nextPathStep",
"title": "CodeQL: Show Next Step on Path"
@@ -335,6 +353,11 @@
"command": "codeQLDatabases.chooseDatabaseInternet",
"when": "view == codeQLDatabases",
"group": "navigation"
},
{
"command": "codeQLDatabases.chooseDatabaseLgtm",
"when": "view == codeQLDatabases",
"group": "navigation"
}
],
"view/item/context": [
@@ -388,6 +411,11 @@
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory"
},
{
"command": "codeQLQueryHistory.viewSarif",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && viewItem == interpretedResultsItem"
},
{
"command": "codeQLTests.showOutputDifferences",
"group": "qltest@1",
@@ -460,6 +488,10 @@
"command": "codeQLDatabases.chooseDatabaseInternet",
"when": "false"
},
{
"command": "codeQLDatabases.chooseDatabaseLgtm",
"when": "false"
},
{
"command": "codeQLDatabases.upgradeDatabase",
"when": "false"
@@ -484,6 +516,10 @@
"command": "codeQLQueryHistory.showQueryText",
"when": "false"
},
{
"command": "codeQLQueryHistory.viewSarif",
"when": "false"
},
{
"command": "codeQLQueryHistory.setLabel",
"when": "false"
@@ -597,7 +633,7 @@
"typescript-config": "^0.0.1",
"typescript-formatter": "^7.2.2",
"vsce": "^1.65.0",
"vscode-test": "^1.0.0",
"vscode-test": "^1.4.0",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.2",
"eslint": "~6.8.0",

View File

@@ -173,7 +173,7 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
const ref = decodeSourceArchiveUri(uri);
const archive = await this.getArchive(ref.sourceArchiveZipPath);
const contents = archive.dirMap.get(ref.pathWithinSourceArchive);
const result = contents === undefined ? [] : Array.from(contents.entries());
const result = contents === undefined ? undefined : Array.from(contents.entries());
if (result === undefined) {
throw vscode.FileSystemError.FileNotFound(uri);
}
@@ -238,7 +238,7 @@ export class ArchiveFileSystemProvider implements vscode.FileSystemProvider {
if (archive.dirMap.has(reqPath)) {
return new Directory(reqPath);
}
throw vscode.FileSystemError.FileNotFound(uri);
throw vscode.FileSystemError.FileNotFound(`uri '${uri.toString()}', interpreted as '${reqPath}' in archive '${ref.sourceArchiveZipPath}'`);
}
private async _lookupAsFile(uri: vscode.Uri): Promise<File> {

View File

@@ -1,10 +1,22 @@
import * as fetch from "node-fetch";
import fetch, { Response } from "node-fetch";
import * as unzipper from "unzipper";
import { Uri, ProgressOptions, ProgressLocation, commands, window } from "vscode";
import {
Uri,
ProgressOptions,
ProgressLocation,
commands,
window,
} from "vscode";
import * as fs from "fs-extra";
import * as path from "path";
import { DatabaseManager, DatabaseItem } from "./databases";
import { ProgressCallback, showAndLogErrorMessage, withProgress, showAndLogInformationMessage } from "./helpers";
import {
ProgressCallback,
showAndLogErrorMessage,
withProgress,
showAndLogInformationMessage,
} from "./helpers";
import { logger } from "./logging";
/**
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
@@ -12,25 +24,39 @@ import { ProgressCallback, showAndLogErrorMessage, withProgress, showAndLogInfor
* @param databasesManager the DatabaseManager
* @param storagePath where to store the unzipped database.
*/
export async function promptImportInternetDatabase(databasesManager: DatabaseManager, storagePath: string): Promise<DatabaseItem | undefined> {
export async function promptImportInternetDatabase(
databasesManager: DatabaseManager,
storagePath: string
): Promise<DatabaseItem | undefined> {
let item: DatabaseItem | undefined = undefined;
try {
const databaseUrl = await window.showInputBox({
prompt: 'Enter URL of zipfile of database to download'
prompt: "Enter URL of zipfile of database to download",
});
if (databaseUrl) {
validateHttpsUrl(databaseUrl);
const progressOptions: ProgressOptions = {
location: ProgressLocation.Notification,
title: 'Adding database from URL',
title: "Adding database from URL",
cancellable: false,
};
await withProgress(progressOptions, async progress => (item = await databaseArchiveFetcher(databaseUrl, databasesManager, storagePath, progress)));
commands.executeCommand('codeQLDatabases.focus');
await withProgress(
progressOptions,
async (progress) =>
(item = await databaseArchiveFetcher(
databaseUrl,
databasesManager,
storagePath,
progress
))
);
commands.executeCommand("codeQLDatabases.focus");
}
showAndLogInformationMessage('Database downloaded and imported successfully.');
showAndLogInformationMessage(
"Database downloaded and imported successfully."
);
} catch (e) {
showAndLogErrorMessage(e.message);
}
@@ -38,6 +64,62 @@ export async function promptImportInternetDatabase(databasesManager: DatabaseMan
return item;
}
/**
* Prompts a user to fetch a database from lgtm.
* User enters a project url and then the user is asked which language
* to download (if there is more than one)
*
* @param databasesManager the DatabaseManager
* @param storagePath where to store the unzipped database.
*/
export async function promptImportLgtmDatabase(
databasesManager: DatabaseManager,
storagePath: string
): Promise<DatabaseItem | undefined> {
let item: DatabaseItem | undefined = undefined;
try {
const lgtmUrl = await window.showInputBox({
prompt:
"Enter the project URL on LGTM (e.g., https://lgtm.com/projects/g/github/codeql)",
});
if (!lgtmUrl) {
return;
}
if (looksLikeLgtmUrl(lgtmUrl)) {
const databaseUrl = await convertToDatabaseUrl(lgtmUrl);
if (databaseUrl) {
const progressOptions: ProgressOptions = {
location: ProgressLocation.Notification,
title: "Adding database from LGTM",
cancellable: false,
};
await withProgress(
progressOptions,
async (progress) =>
(item = await databaseArchiveFetcher(
databaseUrl,
databasesManager,
storagePath,
progress
))
);
commands.executeCommand("codeQLDatabases.focus");
}
} else {
throw new Error(`Invalid LGTM URL: ${lgtmUrl}`);
}
if (item) {
showAndLogInformationMessage(
"Database downloaded and imported successfully."
);
}
} catch (e) {
showAndLogErrorMessage(e.message);
}
return item;
}
/**
* Imports a database from a local archive.
@@ -46,25 +128,41 @@ export async function promptImportInternetDatabase(databasesManager: DatabaseMan
* @param databasesManager the DatabaseManager
* @param storagePath where to store the unzipped database.
*/
export async function importArchiveDatabase(databaseUrl: string, databasesManager: DatabaseManager, storagePath: string): Promise<DatabaseItem | undefined> {
export async function importArchiveDatabase(
databaseUrl: string,
databasesManager: DatabaseManager,
storagePath: string
): Promise<DatabaseItem | undefined> {
let item: DatabaseItem | undefined = undefined;
try {
const progressOptions: ProgressOptions = {
location: ProgressLocation.Notification,
title: 'Importing database from archive',
title: "Importing database from archive",
cancellable: false,
};
await withProgress(progressOptions, async progress => (item = await databaseArchiveFetcher(databaseUrl, databasesManager, storagePath, progress)));
commands.executeCommand('codeQLDatabases.focus');
await withProgress(
progressOptions,
async (progress) =>
(item = await databaseArchiveFetcher(
databaseUrl,
databasesManager,
storagePath,
progress
))
);
commands.executeCommand("codeQLDatabases.focus");
showAndLogInformationMessage('Database unzipped and imported successfully.');
if (item) {
showAndLogInformationMessage(
"Database unzipped and imported successfully."
);
}
} catch (e) {
showAndLogErrorMessage(e.message);
}
return item;
}
/**
* Fetches an archive database. The database might be on the internet
* or in the local filesystem.
@@ -82,8 +180,8 @@ async function databaseArchiveFetcher(
): Promise<DatabaseItem> {
progressCallback?.({
maxStep: 3,
message: 'Getting database',
step: 1
message: "Getting database",
step: 1,
});
if (!storagePath) {
throw new Error("No storage path specified.");
@@ -99,18 +197,22 @@ async function databaseArchiveFetcher(
progressCallback?.({
maxStep: 3,
message: 'Opening database',
step: 3
message: "Opening database",
step: 3,
});
// find the path to the database. The actual database might be in a sub-folder
const dbPath = await findDirWithFile(unzipPath, '.dbinfo', 'codeql-database.yml');
const dbPath = await findDirWithFile(
unzipPath,
".dbinfo",
"codeql-database.yml"
);
if (dbPath) {
const item = await databasesManager.openDatabase(Uri.parse(`file:${dbPath}`));
const item = await databasesManager.openDatabase(Uri.file(dbPath));
databasesManager.setCurrentDatabaseItem(item);
return item;
} else {
throw new Error('Database not found in archive.');
throw new Error("Database not found in archive.");
}
}
@@ -141,7 +243,6 @@ async function getStorageFolder(storagePath: string, urlStr: string) {
return folderName;
}
function validateHttpsUrl(databaseUrl: string) {
let uri;
try {
@@ -150,47 +251,78 @@ function validateHttpsUrl(databaseUrl: string) {
throw new Error(`Invalid url: ${databaseUrl}`);
}
if (uri.scheme !== 'https') {
throw new Error('Must use https for downloading a database.');
if (uri.scheme !== "https") {
throw new Error("Must use https for downloading a database.");
}
}
async function readAndUnzip(databaseUrl: string, unzipPath: string) {
const unzipStream = unzipper.Extract({
path: unzipPath
path: unzipPath,
});
await new Promise((resolve, reject) => {
// we already know this is a file scheme
const databaseFile = Uri.parse(databaseUrl).fsPath;
const stream = fs.createReadStream(databaseFile);
stream.on('error', reject);
unzipStream.on('error', reject);
unzipStream.on('close', resolve);
stream.on("error", reject);
unzipStream.on("error", reject);
unzipStream.on("close", resolve);
stream.pipe(unzipStream);
});
}
async function fetchAndUnzip(databaseUrl: string, unzipPath: string, progressCallback?: ProgressCallback) {
const response = await fetch.default(databaseUrl);
async function fetchAndUnzip(
databaseUrl: string,
unzipPath: string,
progressCallback?: ProgressCallback
) {
const response = await fetch(databaseUrl);
await checkForFailingResponse(response);
const unzipStream = unzipper.Extract({
path: unzipPath
path: unzipPath,
});
progressCallback?.({
maxStep: 3,
message: 'Unzipping database',
step: 2
message: "Unzipping database",
step: 2,
});
await new Promise((resolve, reject) => {
response.body.on('error', reject);
unzipStream.on('error', reject);
unzipStream.on('close', resolve);
const handler = (err: Error) => {
if (err.message.startsWith('invalid signature')) {
reject(new Error('Not a valid archive.'));
} else {
reject(err);
}
};
response.body.on("error", handler);
unzipStream.on("error", handler);
unzipStream.on("close", resolve);
response.body.pipe(unzipStream);
});
}
async function checkForFailingResponse(response: Response): Promise<void | never> {
if (response.ok) {
return;
}
// An error downloading the database. Attempt to extract the resaon behind it.
const text = await response.text();
let msg: string;
try {
const obj = JSON.parse(text);
msg = obj.error || obj.message || obj.reason || JSON.stringify(obj, null, 2);
} catch (e) {
msg = text;
}
throw new Error(`Error downloading database.\n\nReason: ${msg}`);
}
function isFile(databaseUrl: string) {
return Uri.parse(databaseUrl).scheme === 'file';
return Uri.parse(databaseUrl).scheme === "file";
}
/**
@@ -201,12 +333,16 @@ function isFile(databaseUrl: string) {
*
* @returns the directory containing the file, or undefined if not found.
*/
async function findDirWithFile(dir: string, ...toFind: string[]): Promise<string | undefined> {
// exported for testing
export async function findDirWithFile(
dir: string,
...toFind: string[]
): Promise<string | undefined> {
if (!(await fs.stat(dir)).isDirectory()) {
return;
}
const files = await fs.readdir(dir);
if (toFind.some(file => files.includes(file))) {
if (toFind.some((file) => files.includes(file))) {
return dir;
}
for (const file of files) {
@@ -218,3 +354,89 @@ async function findDirWithFile(dir: string, ...toFind: string[]): Promise<string
}
return;
}
/**
* The URL pattern is https://lgtm.com/projects/{provider}/{org}/{name}/{irrelevant-subpages}.
* There are several possibilities for the provider: in addition to GitHub.com(g),
* LGTM currently hosts projects from Bitbucket (b), GitLab (gl) and plain git (git).
*
* After the {provider}/{org}/{name} path components, there may be the components
* related to sub pages.
*
* This function accepts any url that matches the patter above
*
* @param lgtmUrl The URL to the lgtm project
*
* @return true if this looks like an LGTM project url
*/
// exported for testing
export function looksLikeLgtmUrl(lgtmUrl: string | undefined): lgtmUrl is string {
if (!lgtmUrl) {
return false;
}
try {
const uri = Uri.parse(lgtmUrl, true);
if (uri.scheme !== "https") {
return false;
}
if (uri.authority !== "lgtm.com" && uri.authority !== "www.lgtm.com") {
return false;
}
const paths = uri.path.split("/").filter((segment) => segment);
return paths.length >= 4 && paths[0] === "projects";
} catch (e) {
return false;
}
}
// exported for testing
export async function convertToDatabaseUrl(lgtmUrl: string) {
try {
const uri = Uri.parse(lgtmUrl, true);
const paths = ["api", "v1.0"].concat(
uri.path.split("/").filter((segment) => segment)
).slice(0, 6);
const projectUrl = `https://lgtm.com/${paths.join("/")}`;
const projectResponse = await fetch(projectUrl);
const projectJson = await projectResponse.json();
if (projectJson.code === 404) {
throw new Error();
}
const language = await promptForLanguage(projectJson);
if (!language) {
return;
}
return `https://lgtm.com/${[
"api",
"v1.0",
"snapshots",
projectJson.id,
language,
].join("/")}`;
} catch (e) {
logger.log(`Error: ${e.message}`);
throw new Error(`Invalid LGTM URL: ${lgtmUrl}`);
}
}
async function promptForLanguage(
projectJson: any
): Promise<string | undefined> {
if (!projectJson?.languages?.length) {
return;
}
if (projectJson.languages.length === 1) {
return projectJson.languages[0].language;
}
return await window.showQuickPick(
projectJson.languages.map((lang: { language: string }) => lang.language), {
placeHolder: "Select the database language to download:"
}
);
}

View File

@@ -8,7 +8,8 @@ import { logger } from './logging';
import { clearCacheInDatabase, UserCancellationException } from './run-queries';
import * as qsClient from './queryserver-client';
import { upgradeDatabase } from './upgrades';
import { importArchiveDatabase, promptImportInternetDatabase } from './databaseFetcher';
import { importArchiveDatabase, promptImportInternetDatabase, promptImportLgtmDatabase } from './databaseFetcher';
import * as fs from 'fs-extra';
type ThemableIconPath = { light: string; dark: string } | string;
@@ -178,6 +179,7 @@ export class DatabaseUI extends DisposableObject {
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseFolder', this.handleChooseDatabaseFolder));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseArchive', this.handleChooseDatabaseArchive));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseInternet', this.handleChooseDatabaseInternet));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.chooseDatabaseLgtm', this.handleChooseDatabaseLgtm));
ctx.subscriptions.push(commands.registerCommand('codeQL.setCurrentDatabase', this.handleSetCurrentDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQL.upgradeCurrentDatabase', this.handleUpgradeCurrentDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQL.clearCache', this.handleClearCache));
@@ -216,6 +218,10 @@ export class DatabaseUI extends DisposableObject {
return await promptImportInternetDatabase(this.databaseManager, this.storagePath);
}
handleChooseDatabaseLgtm = async (): Promise<DatabaseItem | undefined> => {
return await promptImportLgtmDatabase(this.databaseManager, this.storagePath);
}
private handleSortByName = async () => {
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc) {
this.treeDataProvider.sortOrder = SortOrder.NameDesc;
@@ -361,8 +367,9 @@ export class DatabaseUI extends DisposableObject {
}
if (byFolder) {
const fixedUri = await this.fixDbUri(uri);
// we are selecting a database folder
return await this.setCurrentDatabase(uri);
return await this.setCurrentDatabase(fixedUri);
}
else {
// we are selecting a database archive. Must unzip into a workspace-controlled area
@@ -370,4 +377,26 @@ export class DatabaseUI extends DisposableObject {
return await importArchiveDatabase(uri.toString(true), this.databaseManager, this.storagePath);
}
}
/**
* Perform some heuristics to ensure a proper database location is chosen.
*
* 1. If the selected URI to add is a file, choose the containing directory
* 2. If the selected URI is a directory matching db-*, choose the containing directory
* 3. choose the current directory
*
* @param uri a URI that is a datbase folder or inside it
*
* @return the actual database folder found by using the heuristics above.
*/
private async fixDbUri(uri: Uri): Promise<Uri> {
let dbPath = uri.fsPath;
if ((await fs.stat(dbPath)).isFile()) {
dbPath = path.dirname(dbPath);
}
if (path.basename(dbPath).startsWith('db-')) {
dbPath = path.dirname(dbPath);
}
return Uri.file(dbPath);
}
}

View File

@@ -34,6 +34,13 @@ function tagOfKeyType(keyType: KeyType): string {
}
}
function nameOfKeyType(keyType: KeyType): string {
switch (keyType) {
case KeyType.DefinitionQuery: return "definitions";
case KeyType.ReferenceQuery: return "references";
}
}
async function resolveQueries(cli: CodeQLCliServer, qlpack: string, keyType: KeyType): Promise<string[]> {
const suiteFile = tmp.fileSync({ postfix: '.qls' }).name;
const suiteYaml = { qlpack, include: { kind: 'definitions', 'tags contain': tagOfKeyType(keyType) } };
@@ -41,7 +48,10 @@ async function resolveQueries(cli: CodeQLCliServer, qlpack: string, keyType: Key
const queries = await cli.resolveQueriesInSuite(suiteFile, helpers.getOnDiskWorkspaceFolders());
if (queries.length === 0) {
throw new Error("Couldn't find any queries for qlpack");
vscode.window.showErrorMessage(
`No ${nameOfKeyType(keyType)} queries (tagged "${tagOfKeyType(keyType)}") could be found in the current library path. It might be necessary to upgrade the CodeQL libraries.`
);
throw new Error(`Couldn't find any queries tagged ${tagOfKeyType(keyType)} for qlpack ${qlpack}`);
}
return queries;
}

View File

@@ -4,6 +4,7 @@ import { testExplorerExtensionId, TestHub } from 'vscode-test-adapter-api';
import * as archiveFilesystemProvider from './archive-filesystem-provider';
import { CodeQLCliServer } from './cli';
import { DistributionConfigListener, QueryHistoryConfigListener, QueryServerConfigListener } from './config';
import * as languageSupport from './languageSupport';
import { DatabaseManager } from './databases';
import { DatabaseUI } from './databases-ui';
import { TemplateQueryDefinitionProvider, TemplateQueryReferenceProvider } from './definitions';
@@ -78,6 +79,7 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
logger.log('Starting CodeQL extension');
initializeLogging(ctx);
languageSupport.install();
const distributionConfigListener = new DistributionConfigListener();
ctx.subscriptions.push(distributionConfigListener);
@@ -347,6 +349,7 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
}));
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseFolder', () => databaseUI.handleChooseDatabaseFolder()));
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseArchive', () => databaseUI.handleChooseDatabaseArchive()));
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseLgtm', () => databaseUI.handleChooseDatabaseLgtm()));
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseInternet', () => databaseUI.handleChooseDatabaseInternet()));
logger.log('Starting language server.');

View File

@@ -402,7 +402,7 @@ export class InterfaceManager extends DisposableObject {
const sarif = await interpretResults(
this.cliServer,
metadata,
resultsPaths.resultsPath,
resultsPaths,
sourceInfo
);
// For performance reasons, limit the number of results we try
@@ -440,7 +440,7 @@ export class InterfaceManager extends DisposableObject {
): Promise<Interpretation | undefined> {
let interpretation: Interpretation | undefined = undefined;
if (
(await query.hasInterpretedResults()) &&
(await query.canHaveInterpretedResults()) &&
query.quickEvalPosition === undefined // never do results interpretation if quickEval
) {
try {

View File

@@ -0,0 +1,52 @@
import { IndentAction, languages } from "vscode";
/**
* OnEnterRules are available in language-configurations, but you cannot specify them in the language-configuration.json.
* They can only be specified programmatically.
*
* Also, we should keep the language-configuration.json as a json file and register it in the package.json because
* it is registered first, before the extension is activated, so language features are available quicker.
*
* See https://github.com/microsoft/vscode/issues/11514
* See https://github.com/microsoft/vscode/blob/master/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts
*/
export function install() {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const langConfig = require('../language-configuration.json');
// setLanguageConfiguration requires a regexp for the wordpattern, not a string
langConfig.wordPattern = new RegExp(langConfig.wordPattern);
langConfig.onEnterRules = onEnterRules;
languages.setLanguageConfiguration('ql', langConfig);
languages.setLanguageConfiguration('qll', langConfig);
languages.setLanguageConfiguration('dbscheme', langConfig);
}
const onEnterRules = [
{
// e.g. /** | */
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
afterText: /^\s*\*\/$/,
action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' }
}, {
// e.g. /** ...|
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
action: { indentAction: IndentAction.None, appendText: ' * ' }
}, {
// e.g. * ...|
beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/,
oneLineAboveText: /^(\s*(\/\*\*|\*)).*/,
action: { indentAction: IndentAction.None, appendText: '* ' }
}, {
// e.g. */|
beforeText: /^(\t|[ ])*[ ]\*\/\s*$/,
action: { indentAction: IndentAction.None, removeText: 1 }
},
{
// e.g. *-----*/|
beforeText: /^(\t|[ ])*[ ]\*[^/]*\*\/\s*$/,
action: { indentAction: IndentAction.None, removeText: 1 }
}
];

View File

@@ -74,7 +74,7 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<CompletedQuery>
constructor(private ctx: ExtensionContext) {
}
getTreeItem(element: CompletedQuery): vscode.TreeItem {
async getTreeItem(element: CompletedQuery): Promise<vscode.TreeItem> {
const it = new vscode.TreeItem(element.toString());
it.command = {
@@ -83,6 +83,11 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<CompletedQuery>
arguments: [element],
};
// Mark this query history item according to whether it has a
// SARIF file so that we can make context menu items conditionally
// available.
it.contextValue = await element.query.hasInterpretedResults() ? 'interpretedResultsItem' : 'rawResultsItem';
if (!element.didRunSuccessfully) {
it.iconPath = path.join(this.ctx.extensionPath, FAILED_QUERY_HISTORY_ITEM_ICON);
}
@@ -257,6 +262,22 @@ export class QueryHistoryManager {
}
}
async handleViewSarif(queryHistoryItem: CompletedQuery) {
try {
const hasInterpretedResults = await queryHistoryItem.query.canHaveInterpretedResults();
if (hasInterpretedResults) {
const textDocument = await vscode.workspace.openTextDocument(vscode.Uri.file(queryHistoryItem.query.resultsPaths.interpretedResultsPath));
await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One);
}
else {
const label = queryHistoryItem.getLabel();
helpers.showAndLogInformationMessage(`Query ${label} has no interpreted results.`);
}
} catch (e) {
helpers.showAndLogErrorMessage(e.message);
}
}
async getQueryText(queryHistoryItem: CompletedQuery): Promise<string> {
if (queryHistoryItem.options.queryText) {
return queryHistoryItem.options.queryText;
@@ -296,6 +317,7 @@ export class QueryHistoryManager {
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryText', this.handleShowQueryText.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.viewSarif', this.handleViewSarif.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
return this.handleItemClicked(item);
}));

View File

@@ -5,7 +5,7 @@ import * as cli from './cli';
import * as sarif from 'sarif';
import * as fs from 'fs-extra';
import * as path from 'path';
import { RawResultsSortState, SortedResultSetInfo, DatabaseInfo, QueryMetadata, InterpretedResultsSortState } from "./interface-types";
import { RawResultsSortState, SortedResultSetInfo, DatabaseInfo, QueryMetadata, InterpretedResultsSortState, ResultsPaths } from "./interface-types";
import { QueryHistoryConfig } from "./config";
import { QueryHistoryItemOptions } from "./query-history";
@@ -54,13 +54,6 @@ export class CompletedQuery implements QueryWithResults {
return helpers.getQueryName(this.query);
}
/**
* Holds if this query should produce interpreted results.
*/
canInterpretedResults(): Promise<boolean> {
return this.query.dbItem.hasMetadataFile();
}
get statusString(): string {
switch (this.result.resultType) {
case messages.QueryResultType.CANCELLATION:
@@ -130,9 +123,8 @@ export class CompletedQuery implements QueryWithResults {
/**
* Call cli command to interpret results.
*/
export async function interpretResults(server: cli.CodeQLCliServer, metadata: QueryMetadata | undefined, resultsPath: string, sourceInfo?: cli.SourceInfo): Promise<sarif.Log> {
const interpretedResultsPath = resultsPath + ".interpreted.sarif";
export async function interpretResults(server: cli.CodeQLCliServer, metadata: QueryMetadata | undefined, resultsPaths: ResultsPaths, sourceInfo?: cli.SourceInfo): Promise<sarif.Log> {
const { resultsPath, interpretedResultsPath } = resultsPaths;
if (await fs.pathExists(interpretedResultsPath)) {
return JSON.parse(await fs.readFile(interpretedResultsPath, 'utf8'));
}

View File

@@ -157,15 +157,22 @@ export class QueryInfo {
}
/**
* Holds if this query should produce interpreted results.
* Holds if this query can in principle produce interpreted results.
*/
async hasInterpretedResults(): Promise<boolean> {
async canHaveInterpretedResults(): Promise<boolean> {
const hasMetadataFile = await this.dbItem.hasMetadataFile();
if (!hasMetadataFile) {
logger.log("Cannot produce interpreted results since the database does not have a .dbinfo or codeql-database.yml file.");
}
return hasMetadataFile;
}
/**
* Holds if this query actually has produced interpreted results.
*/
async hasInterpretedResults(): Promise<boolean> {
return fs.pathExists(this.resultsPaths.interpretedResultsPath);
}
}
export interface QueryWithResults {

View File

@@ -1,8 +1,10 @@
import { expect } from "chai";
import * as path from "path";
import { ArchiveFileSystemProvider, decodeSourceArchiveUri, encodeSourceArchiveUri, ZipFileReference } from "../../archive-filesystem-provider";
import { expect } from 'chai';
import * as path from 'path';
describe("archive filesystem provider", () => {
import { encodeSourceArchiveUri, ArchiveFileSystemProvider, decodeSourceArchiveUri, ZipFileReference } from '../../archive-filesystem-provider';
import { FileType, FileSystemError } from 'vscode';
describe('archive-filesystem-provider', () => {
it("reads empty file correctly", async () => {
const archiveProvider = new ArchiveFileSystemProvider();
const uri = encodeSourceArchiveUri({
@@ -12,6 +14,98 @@ describe("archive filesystem provider", () => {
const data = await archiveProvider.readFile(uri);
expect(data.length).to.equal(0);
});
it("read non-empty file correctly", async () => {
const archiveProvider = new ArchiveFileSystemProvider();
const uri = encodeSourceArchiveUri({
sourceArchiveZipPath: path.resolve(__dirname, "data/archive-filesystem-provider-test/zip_with_folder.zip"),
pathWithinSourceArchive: "folder1/textFile.txt"
});
const data = await archiveProvider.readFile(uri);
expect(Buffer.from(data).toString('utf8')).to.be.equal('I am a text\n');
});
it("read a directory", async () => {
const archiveProvider = new ArchiveFileSystemProvider();
const uri = encodeSourceArchiveUri({
sourceArchiveZipPath: path.resolve(__dirname, "data/archive-filesystem-provider-test/zip_with_folder.zip"),
pathWithinSourceArchive: "folder1"
});
const files = await archiveProvider.readDirectory(uri);
expect(files).to.be.deep.equal([
['folder2', FileType.Directory],
['textFile.txt', FileType.File],
['textFile2.txt', FileType.File],
]);
});
it('should handle a missing directory', async () => {
const archiveProvider = new ArchiveFileSystemProvider();
const uri = encodeSourceArchiveUri({
sourceArchiveZipPath: path.resolve(__dirname, "data/archive-filesystem-provider-test/zip_with_folder.zip"),
pathWithinSourceArchive: "folder1/not-here"
});
try {
await archiveProvider.readDirectory(uri);
throw new Error('Failed');
} catch (e) {
expect(e).to.be.instanceOf(FileSystemError);
}
});
it('should handle a missing file', async () => {
const archiveProvider = new ArchiveFileSystemProvider();
const uri = encodeSourceArchiveUri({
sourceArchiveZipPath: path.resolve(__dirname, "data/archive-filesystem-provider-test/zip_with_folder.zip"),
pathWithinSourceArchive: "folder1/not-here"
});
try {
await archiveProvider.readFile(uri);
throw new Error('Failed');
} catch (e) {
expect(e).to.be.instanceOf(FileSystemError);
}
});
it('should handle reading a file as a directory', async () => {
const archiveProvider = new ArchiveFileSystemProvider();
const uri = encodeSourceArchiveUri({
sourceArchiveZipPath: path.resolve(__dirname, "data/archive-filesystem-provider-test/zip_with_folder.zip"),
pathWithinSourceArchive: "folder1/textFile.txt"
});
try {
await archiveProvider.readDirectory(uri);
throw new Error('Failed');
} catch (e) {
expect(e).to.be.instanceOf(FileSystemError);
}
});
it('should handle reading a directory as a file', async () => {
const archiveProvider = new ArchiveFileSystemProvider();
const uri = encodeSourceArchiveUri({
sourceArchiveZipPath: path.resolve(__dirname, "data/archive-filesystem-provider-test/zip_with_folder.zip"),
pathWithinSourceArchive: "folder1/folder2"
});
try {
await archiveProvider.readFile(uri);
throw new Error('Failed');
} catch (e) {
expect(e).to.be.instanceOf(FileSystemError);
}
});
it("read a nested directory", async () => {
const archiveProvider = new ArchiveFileSystemProvider();
const uri = encodeSourceArchiveUri({
sourceArchiveZipPath: path.resolve(__dirname, "data/archive-filesystem-provider-test/zip_with_folder.zip"),
pathWithinSourceArchive: "folder1/folder2"
});
const files = await archiveProvider.readDirectory(uri);
expect(files).to.be.deep.equal([
['textFile3.txt', FileType.File],
]);
});
});
describe('source archive uri encoding', function() {

View File

@@ -0,0 +1,140 @@
import "vscode-test";
import "mocha";
import * as chaiAsPromised from "chai-as-promised";
import * as sinon from "sinon";
// import * as sinonChai from 'sinon-chai';
import * as path from "path";
import * as fs from "fs-extra";
import * as tmp from "tmp";
import * as chai from "chai";
import { window } from "vscode";
import {
convertToDatabaseUrl,
looksLikeLgtmUrl,
findDirWithFile,
} from "../../databaseFetcher";
chai.use(chaiAsPromised);
const expect = chai.expect;
describe("databaseFetcher", () => {
describe("convertToDatabaseUrl", () => {
let quickPickSpy: sinon.SinonStub;
beforeEach(() => {
quickPickSpy = sinon.stub(window, "showQuickPick");
});
afterEach(() => {
(window.showQuickPick as sinon.SinonStub).restore();
});
it("should convert a project url to a database url", async () => {
quickPickSpy.returns("javascript" as any);
const lgtmUrl = "https://lgtm.com/projects/g/github/codeql";
const dbUrl = await convertToDatabaseUrl(lgtmUrl);
expect(dbUrl).to.equal(
"https://lgtm.com/api/v1.0/snapshots/1506465042581/javascript"
);
expect(quickPickSpy.firstCall.args[0]).to.contain("javascript");
expect(quickPickSpy.firstCall.args[0]).to.contain("python");
});
it("should convert a project url to a database url with extra path segments", async () => {
quickPickSpy.returns("python" as any);
const lgtmUrl =
"https://lgtm.com/projects/g/github/codeql/subpage/subpage2?query=xxx";
const dbUrl = await convertToDatabaseUrl(lgtmUrl);
expect(dbUrl).to.equal(
"https://lgtm.com/api/v1.0/snapshots/1506465042581/python"
);
});
it("should fail on a nonexistant prohect", async () => {
quickPickSpy.returns("javascript" as any);
const lgtmUrl = "https://lgtm.com/projects/g/github/hucairz";
expect(convertToDatabaseUrl(lgtmUrl)).to.rejectedWith(/Invalid LGTM URL/);
});
});
describe("looksLikeLgtmUrl", () => {
it("should handle invalid urls", () => {
expect(looksLikeLgtmUrl("")).to.be.false;
expect(looksLikeLgtmUrl("http://lgtm.com/projects/g/github/codeql")).to.be
.false;
expect(looksLikeLgtmUrl("https://ww.lgtm.com/projects/g/github/codeql"))
.to.be.false;
expect(looksLikeLgtmUrl("https://ww.lgtm.com/projects/g/github")).to.be
.false;
});
it("should handle valid urls", () => {
expect(looksLikeLgtmUrl("https://lgtm.com/projects/g/github/codeql")).to
.be.true;
expect(looksLikeLgtmUrl("https://www.lgtm.com/projects/g/github/codeql"))
.to.be.true;
expect(
looksLikeLgtmUrl("https://lgtm.com/projects/g/github/codeql/sub/pages")
).to.be.true;
expect(
looksLikeLgtmUrl(
"https://lgtm.com/projects/g/github/codeql/sub/pages?query=string"
)
).to.be.true;
});
});
describe("findDirWithFile", () => {
let dir: tmp.DirResult;
beforeEach(() => {
dir = tmp.dirSync({ unsafeCleanup: true });
createFile("a");
createFile("b");
createFile("c");
createDir("dir1");
createFile("dir1", "d");
createFile("dir1", "e");
createFile("dir1", "f");
createDir("dir2");
createFile("dir2", "g");
createFile("dir2", "h");
createFile("dir2", "i");
createDir("dir2", "dir3");
createFile("dir2", "dir3", "j");
createFile("dir2", "dir3", "k");
createFile("dir2", "dir3", "l");
});
it("should find files", async () => {
expect(await findDirWithFile(dir.name, "k")).to.equal(
path.join(dir.name, "dir2", "dir3")
);
expect(await findDirWithFile(dir.name, "h")).to.equal(
path.join(dir.name, "dir2")
);
expect(await findDirWithFile(dir.name, "z", "a")).to.equal(dir.name);
// there's some slight indeterminism when more than one name exists
// but in general, this will find files in the current directory before
// finding files in sub-dirs
expect(await findDirWithFile(dir.name, "k", "a")).to.equal(dir.name);
});
it("should not find files", async () => {
expect(await findDirWithFile(dir.name, "x", "y", "z")).to.be.undefined;
});
function createFile(...segments: string[]) {
fs.createFileSync(path.join(dir.name, ...segments));
}
function createDir(...segments: string[]) {
fs.mkdirSync(path.join(dir.name, ...segments));
}
});
});

View File

@@ -0,0 +1,47 @@
import 'vscode-test';
import 'mocha';
import * as tmp from 'tmp';
import * as path from 'path';
import * as fs from 'fs-extra';
import { expect } from 'chai';
import { Uri } from 'vscode';
import { DatabaseUI } from '../../databases-ui';
describe('databases-ui', () => {
describe('fixDbUri', () => {
const fixDbUri = (DatabaseUI.prototype as any).fixDbUri;
it('should choose current directory direcory normally', async () => {
const dir = tmp.dirSync().name;
const uri = await fixDbUri(Uri.file(dir));
expect(uri.toString()).to.eq(Uri.file(dir).toString());
});
it('should choose parent direcory when file is selected', async () => {
const file = tmp.fileSync().name;
const uri = await fixDbUri(Uri.file(file));
expect(uri.toString()).to.eq(Uri.file(path.dirname(file)).toString());
});
it('should choose parent direcory when db-* is selected', async () => {
const dir = tmp.dirSync().name;
const dbDir = path.join(dir, 'db-hucairz');
await fs.mkdirs(dbDir);
const uri = await fixDbUri(Uri.file(dbDir));
expect(uri.toString()).to.eq(Uri.file(dir).toString());
});
it('should choose parent\'s parent direcory when file selected is in db-*', async () => {
const dir = tmp.dirSync().name;
const dbDir = path.join(dir, 'db-hucairz');
const file = path.join(dbDir, 'nested');
await fs.mkdirs(dbDir);
await fs.createFile(file);
const uri = await fixDbUri(Uri.file(file));
expect(uri.toString()).to.eq(Uri.file(dir).toString());
});
});
});

View File

@@ -11,25 +11,24 @@ type Suite = {
};
/**
* Run an integration test suite `suite` at most `tries` times, or
* until it succeeds, whichever comes first.
*
* TODO: Presently there is no way to distinguish a legitimately
* failed test run from the test runner being terminated by a signal.
* If in the future there arises a way to distinguish these cases
* (e.g. https://github.com/microsoft/vscode-test/pull/56) only retry
* in the terminated-by-signal case.
* Run an integration test suite `suite`, retrying if it segfaults, at
* most `tries` times.
*/
async function runTestsWithRetry(suite: Suite, tries: number): Promise<void> {
async function runTestsWithRetryOnSegfault(suite: Suite, tries: number): Promise<void> {
for (let t = 0; t < tries; t++) {
try {
// Download and unzip VS Code if necessary, and run the integration test suite.
await runTests(suite);
return;
} catch (err) {
console.error(`Exception raised while running tests: ${err}`);
if (t < tries - 1)
console.log('Retrying...');
if (err === 'SIGSEGV') {
console.error('Test runner segfaulted.');
if (t < tries - 1)
console.error('Retrying...');
}
else {
throw err;
}
}
}
console.error(`Tried running suite ${tries} time(s), still failed, giving up.`);
@@ -67,7 +66,7 @@ async function main() {
];
for (const integrationTestSuite of integrationTestSuites) {
await runTestsWithRetry(integrationTestSuite, 3);
await runTestsWithRetryOnSegfault(integrationTestSuite, 3);
}
} catch (err) {
console.error(`Unexpected exception while running tests: ${err}`);

View File

@@ -1,3 +1,3 @@
predicate foo() {
1 == 1
}
1 = 1
}

View File

@@ -9,7 +9,8 @@
"pnpmOptions": {
"strictPeerDependencies": true
},
"nodeSupportedVersionRange": ">=10.13.0 <13.0.0",
"nodeSupportedVersionRange": ">=10.13.0 <15.0.0",
"suppressNodeLtsWarning": true,
"ensureConsistentVersions": true,
"projectFolderMinDepth": 2,
"projectFolderMaxDepth": 2,