Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
379b69a0e9 | ||
|
|
c4353981fa | ||
|
|
cc7fb39be7 | ||
|
|
d8266b7bc1 | ||
|
|
d50277380b | ||
|
|
3e149e7bb3 | ||
|
|
00e252d48a | ||
|
|
6a2832fcc7 | ||
|
|
a7d99cc7e2 | ||
|
|
454e8471a4 | ||
|
|
e2d125a558 | ||
|
|
e345425051 | ||
|
|
0b32961f6d | ||
|
|
e0a58a86fc | ||
|
|
ec45db3bc3 | ||
|
|
94d230308c | ||
|
|
96688e3379 | ||
|
|
88c27618b1 | ||
|
|
11c538a99d | ||
|
|
0e3b7a8eb5 | ||
|
|
65aa6928e4 | ||
|
|
fe02a58e45 | ||
|
|
4030ddbdc2 | ||
|
|
b3642bd62e | ||
|
|
addddb0095 | ||
|
|
d7732c4ed6 | ||
|
|
6e34c03b05 | ||
|
|
75518a5d01 | ||
|
|
4beead54be | ||
|
|
7379f4996a | ||
|
|
c40b8fe1a5 | ||
|
|
210bbcd2e9 | ||
|
|
461892759b | ||
|
|
6277e5cecb | ||
|
|
42ebc3fbe6 | ||
|
|
77b13bd8e3 | ||
|
|
f4e983e214 | ||
|
|
60620a5618 | ||
|
|
25bac72ac5 | ||
|
|
e7ee1f86a8 |
@@ -13,7 +13,7 @@ To see what has changed in the last few versions of the extension, see the [Chan
|
||||
|
||||
* Enables you to use CodeQL to query databases and discover problems in codebases.
|
||||
* 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).
|
||||
* 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.
|
||||
|
||||
|
||||
|
||||
169
common/config/rush/pnpm-lock.yaml
generated
169
common/config/rush/pnpm-lock.yaml
generated
@@ -75,6 +75,7 @@ dependencies:
|
||||
style-loader: 0.23.1
|
||||
through2: 3.0.1
|
||||
tmp: 0.1.0
|
||||
tmp-promise: 3.0.2
|
||||
tree-kill: 1.2.2
|
||||
ts-loader: 5.4.5_typescript@3.8.3
|
||||
ts-node: 8.6.2_typescript@3.8.3
|
||||
@@ -91,6 +92,7 @@ dependencies:
|
||||
vscode-test-adapter-util: 0.7.0
|
||||
webpack: 4.42.0_webpack@4.42.0
|
||||
webpack-cli: 3.3.11_webpack@4.42.0
|
||||
zip-a-folder: 0.0.12
|
||||
lockfileVersion: 5.1
|
||||
packages:
|
||||
/@babel/code-frame/7.8.3:
|
||||
@@ -1034,6 +1036,37 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
|
||||
/archiver-utils/2.1.0:
|
||||
dependencies:
|
||||
glob: 7.1.6
|
||||
graceful-fs: 4.2.3
|
||||
lazystream: 1.0.0
|
||||
lodash.defaults: 4.2.0
|
||||
lodash.difference: 4.5.0
|
||||
lodash.flatten: 4.4.0
|
||||
lodash.isplainobject: 4.0.6
|
||||
lodash.union: 4.6.0
|
||||
normalize-path: 3.0.0
|
||||
readable-stream: 2.3.7
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 6'
|
||||
resolution:
|
||||
integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==
|
||||
/archiver/3.1.1:
|
||||
dependencies:
|
||||
archiver-utils: 2.1.0
|
||||
async: 2.6.3
|
||||
buffer-crc32: 0.2.13
|
||||
glob: 7.1.6
|
||||
readable-stream: 3.6.0
|
||||
tar-stream: 2.1.2
|
||||
zip-stream: 2.1.3
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 6'
|
||||
resolution:
|
||||
integrity: sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==
|
||||
/archy/1.0.0:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -1201,6 +1234,12 @@ packages:
|
||||
node: '>= 0.10'
|
||||
resolution:
|
||||
integrity: sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=
|
||||
/async/2.6.3:
|
||||
dependencies:
|
||||
lodash: 4.17.15
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
|
||||
/asynckit/0.4.0:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -1297,6 +1336,14 @@ packages:
|
||||
optional: true
|
||||
resolution:
|
||||
integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
|
||||
/bl/4.0.2:
|
||||
dependencies:
|
||||
buffer: 5.6.0
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==
|
||||
/bluebird/3.4.7:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -1438,6 +1485,13 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
|
||||
/buffer/5.6.0:
|
||||
dependencies:
|
||||
base64-js: 1.3.1
|
||||
ieee754: 1.1.13
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
|
||||
/buffers/0.1.1:
|
||||
dev: false
|
||||
engines:
|
||||
@@ -1844,6 +1898,17 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||
/compress-commons/2.1.1:
|
||||
dependencies:
|
||||
buffer-crc32: 0.2.13
|
||||
crc32-stream: 3.0.1
|
||||
normalize-path: 3.0.0
|
||||
readable-stream: 2.3.7
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 6'
|
||||
resolution:
|
||||
integrity: sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==
|
||||
/concat-map/0.0.1:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -1918,6 +1983,21 @@ packages:
|
||||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==
|
||||
/crc/3.8.0:
|
||||
dependencies:
|
||||
buffer: 5.6.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==
|
||||
/crc32-stream/3.0.1:
|
||||
dependencies:
|
||||
crc: 3.8.0
|
||||
readable-stream: 3.6.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 6.9.0'
|
||||
resolution:
|
||||
integrity: sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==
|
||||
/create-ecdh/4.0.3:
|
||||
dependencies:
|
||||
bn.js: 4.11.8
|
||||
@@ -3015,6 +3095,10 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
|
||||
/fs-constants/1.0.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||
/fs-extra/7.0.1:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.3
|
||||
@@ -4328,6 +4412,18 @@ packages:
|
||||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
|
||||
/lodash.defaults/4.2.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
|
||||
/lodash.difference/4.5.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=
|
||||
/lodash.flatten/4.4.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
|
||||
/lodash.get/4.4.2:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -4336,6 +4432,14 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
/lodash.isplainobject/4.0.6:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
/lodash.union/4.6.0:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
|
||||
/lodash/4.17.15:
|
||||
dev: false
|
||||
resolution:
|
||||
@@ -6116,6 +6220,13 @@ packages:
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
|
||||
/rimraf/3.0.2:
|
||||
dependencies:
|
||||
glob: 7.1.6
|
||||
dev: false
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||
/ripemd160/2.0.2:
|
||||
dependencies:
|
||||
hash-base: 3.0.4
|
||||
@@ -6806,6 +6917,16 @@ packages:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||
/tar-stream/2.1.2:
|
||||
dependencies:
|
||||
bl: 4.0.2
|
||||
end-of-stream: 1.4.4
|
||||
fs-constants: 1.0.0
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.0
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==
|
||||
/tar/4.4.13:
|
||||
dependencies:
|
||||
chownr: 1.1.4
|
||||
@@ -6917,6 +7038,12 @@ packages:
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
/tmp-promise/3.0.2:
|
||||
dependencies:
|
||||
tmp: 0.2.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA==
|
||||
/tmp/0.0.29:
|
||||
dependencies:
|
||||
os-tmpdir: 1.0.2
|
||||
@@ -6941,6 +7068,14 @@ packages:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==
|
||||
/tmp/0.2.1:
|
||||
dependencies:
|
||||
rimraf: 3.0.2
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=8.17.0'
|
||||
resolution:
|
||||
integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
|
||||
/to-absolute-glob/2.0.2:
|
||||
dependencies:
|
||||
is-absolute: 1.0.0
|
||||
@@ -7822,6 +7957,22 @@ packages:
|
||||
commander: 2.20.3
|
||||
resolution:
|
||||
integrity: sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw==
|
||||
/zip-a-folder/0.0.12:
|
||||
dependencies:
|
||||
archiver: 3.1.1
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-wZGiWgp3z2TocBlzx3S5tsLgPbT39qG2uIZmn2MhYLVjhKIr2nMhg7i4iPDL4W3XvMDaOEEVU5ZB0Y/Pt6BLvA==
|
||||
/zip-stream/2.1.3:
|
||||
dependencies:
|
||||
archiver-utils: 2.1.0
|
||||
compress-commons: 2.1.1
|
||||
readable-stream: 3.6.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>= 6'
|
||||
resolution:
|
||||
integrity: sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==
|
||||
'file:projects/codeql-gulp-tasks.tgz':
|
||||
dependencies:
|
||||
'@microsoft/node-core-library': 3.13.0
|
||||
@@ -7853,7 +8004,7 @@ packages:
|
||||
dev: false
|
||||
name: '@rush-temp/codeql-gulp-tasks'
|
||||
resolution:
|
||||
integrity: sha512-eWHB3hxiYH/b5wdcG0S1CqTBmqtNTu3VVvdEo/qrZxMoPVfXOT630ocQ8CxrqTfT1kyx1ZpKoqzzaR0hxgDqCg==
|
||||
integrity: sha512-fqE1VOiN1SmB17tolFDAlApTd/oXFfl1wh+WkQfajdOsHLxDSMPYAcWnt7FdSaQSp/EgIIZ3lNnDBTXWlPkE/g==
|
||||
tarball: 'file:projects/codeql-gulp-tasks.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/codeql-vscode-utils.tgz_typescript@3.8.3':
|
||||
@@ -7867,7 +8018,7 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
resolution:
|
||||
integrity: sha512-OC9Yx7t16ZzrGmGu1MIV8H4A2RZRFaNonIwQqlWEJ56LGVkNL+gX0RxAPwtT76AbEzhsyb4VUQ609QRt98rPlw==
|
||||
integrity: sha512-UdaU/jwMCYYysISj/rwLWXxZKhSYo/uIkUBM5LpQhm4vy4z/0FPrEqqZWOKk+ldxUPMi8lx+8U7WkqXnTY5PFg==
|
||||
tarball: 'file:projects/codeql-vscode-utils.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/semmle-bqrs.tgz_typescript@3.8.3':
|
||||
@@ -7883,7 +8034,7 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
resolution:
|
||||
integrity: sha512-l46HYR+5UlVb0XIQMNRBjADvVVzFolw3KrPPHuIRWEXhkdnFXLTOHEXBFcnijNZIduePBNqPrII+wqMmRcJVvg==
|
||||
integrity: sha512-xXKv5YnDihfs9L8fWDfHY4IndM8A/cievN+teT/bStvPQ53P3RJikeF2R11+ovg/UNg8YaCQYAHovi4z5KEx4g==
|
||||
tarball: 'file:projects/semmle-bqrs.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/semmle-io-node.tgz_typescript@3.8.3':
|
||||
@@ -7899,7 +8050,7 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
resolution:
|
||||
integrity: sha512-O034F8DYa59REsFjAeP4qxtoGBslO8RiGJK8bdMaxWqOX52IJyVqx6ybNgz3cWzX0Q+KI3UjZTCdk3vvF+Wsbg==
|
||||
integrity: sha512-sB1AHo/3SHXobocIOTOOF4A6RFD7PPr8n8CSv6Qq4ZiOP/8ULNmwgoxEAksp6x4Uf6OSgJ9Puawk/v0DeeK1UQ==
|
||||
tarball: 'file:projects/semmle-io-node.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/semmle-io.tgz_typescript@3.8.3':
|
||||
@@ -7914,14 +8065,14 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
resolution:
|
||||
integrity: sha512-40cXVM0J2z7vTtvon31m8QAd/W7c3P4yLqpoGnbtLcmb2/1d881IdZbe/8dMsUOor/Qv/VsxcL9ndz+Av+eClw==
|
||||
integrity: sha512-LmAIuLFIfrPg81Hv9UZ7VxfiM3jpXJ7HkH8w2L1t8jUZt6piibQbwMo3R8oz1v1F21QnL8DxBG4v7PCHgbf72w==
|
||||
tarball: 'file:projects/semmle-io.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/typescript-config.tgz':
|
||||
dev: false
|
||||
name: '@rush-temp/typescript-config'
|
||||
resolution:
|
||||
integrity: sha512-kSFyvKy63jUHFVXQEzALiYfsTdn7J+Y7PcqtUVo9GndU5b5Xh3rBpVbZD1QN8+y8GfT0m/sdZZQVyH0h+On11Q==
|
||||
integrity: sha512-XuUIySaNoooIduvehnlKYaHqZJmmQoCqB1RtKhNszjCYZaSSJAnKVucViWBf5oNLKSNP7NchrD7gcoBlQ3xYvw==
|
||||
tarball: 'file:projects/typescript-config.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/vscode-codeql.tgz':
|
||||
@@ -7985,6 +8136,7 @@ packages:
|
||||
style-loader: 0.23.1
|
||||
through2: 3.0.1
|
||||
tmp: 0.1.0
|
||||
tmp-promise: 3.0.2
|
||||
tree-kill: 1.2.2
|
||||
ts-loader: 5.4.5_typescript@3.8.3
|
||||
ts-node: 8.6.2_typescript@3.8.3
|
||||
@@ -8000,10 +8152,11 @@ packages:
|
||||
vscode-test-adapter-util: 0.7.0
|
||||
webpack: 4.42.0_webpack@4.42.0
|
||||
webpack-cli: 3.3.11_webpack@4.42.0
|
||||
zip-a-folder: 0.0.12
|
||||
dev: false
|
||||
name: '@rush-temp/vscode-codeql'
|
||||
resolution:
|
||||
integrity: sha512-oGd4/XeaRctrSAFlpSZ3yhqnaTUQ6ZT2gi5iVuDWAB4FMiwJ2RCk/eFPlXrI6d+cl3l9wKHgCQeaLsbx2BfWjw==
|
||||
integrity: sha512-0Y23lFB67k0+FFH/+TkoZDMBgeScx4E73Q/j+oDIsu7G+Dsx0SLp3Lzb/1nKJma9i2t/OFVYbTZiRYPUisIBmA==
|
||||
tarball: 'file:projects/vscode-codeql.tgz'
|
||||
version: 0.0.0
|
||||
registry: ''
|
||||
@@ -8084,6 +8237,7 @@ specifiers:
|
||||
style-loader: ~0.23.1
|
||||
through2: ^3.0.1
|
||||
tmp: ^0.1.0
|
||||
tmp-promise: ~3.0.2
|
||||
tree-kill: ~1.2.2
|
||||
ts-loader: ^5.4.5
|
||||
ts-node: ^8.3.0
|
||||
@@ -8100,3 +8254,4 @@ specifiers:
|
||||
vscode-test-adapter-util: ~0.7.0
|
||||
webpack: ^4.38.0
|
||||
webpack-cli: ^3.3.2
|
||||
zip-a-folder: ~0.0.12
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
# CodeQL for Visual Studio Code: Changelog
|
||||
|
||||
## 1.3.1 - 7 July 2020
|
||||
|
||||
- Fix unzipping of large files.
|
||||
- Ensure compare order is consistent when selecting two queries to compare. The first query selected is always the _from_ query and the query selected later is always the _to_ query.
|
||||
- Ensure added databases have zipped source locations for databases added as archives or downloaded from the internet.
|
||||
- Fix bug where it is not possible to add databases starting with `db-*`.
|
||||
- Change styling of pagination section of the results page.
|
||||
- Fix display of query text for stored quick queries.
|
||||
|
||||
## 1.3.0 - 22 June 2020
|
||||
|
||||
- Report error when selecting invalid database.
|
||||
- Add descriptive message for database archive import failure.
|
||||
- Respect VSCode's i18n locale setting when formatting dates and sorting strings.
|
||||
- Allow the opening of large Sarif files externally from VSCode.
|
||||
- Respect VS Code's i18n locale setting when formatting dates and sorting strings.
|
||||
- Allow the opening of large SARIF files externally from VS Code.
|
||||
- Add new 'CodeQL: Compare Query' command that shows the differences between two queries.
|
||||
- Allow multiple items in the query history view to be removed in one operation.
|
||||
- Allow multiple items in the databases view to be removed in one operation.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "CodeQL for Visual Studio Code",
|
||||
"author": "GitHub",
|
||||
"private": true,
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.1",
|
||||
"publisher": "GitHub",
|
||||
"license": "MIT",
|
||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||
@@ -312,7 +312,7 @@
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.compareWith",
|
||||
"title": "Compare with..."
|
||||
"title": "Compare Results"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryResults.nextPathStep",
|
||||
@@ -612,7 +612,9 @@
|
||||
"vscode-test-adapter-util": "~0.7.0",
|
||||
"minimist": "~1.2.5",
|
||||
"semver": "~7.3.2",
|
||||
"@types/semver": "~7.2.0"
|
||||
"@types/semver": "~7.2.0",
|
||||
"tmp-promise": "~3.0.2",
|
||||
"zip-a-folder": "~0.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.7",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DecodedBqrsChunk, ResultSetSchema, ColumnKind, Column, ColumnValue } from './bqrs-cli-types';
|
||||
import { LocationValue, ResultSetSchema as AdaptedSchema, ColumnSchema, ColumnType, LocationStyle } from 'semmle-bqrs';
|
||||
import { ResultSet } from './interface-types';
|
||||
|
||||
// FIXME: This is a temporary bit of impedance matching to convert
|
||||
// from the types provided by ./bqrs-cli-types, to the types used by
|
||||
@@ -128,7 +129,8 @@ export interface ExtensionParsedResultSets {
|
||||
t: 'ExtensionParsed';
|
||||
pageNumber: number;
|
||||
numPages: number;
|
||||
numInterpretedPages: number;
|
||||
selectedTable?: string; // when undefined, means 'show default table'
|
||||
resultSetNames: string[];
|
||||
resultSet: RawResultSet;
|
||||
resultSet: ResultSet;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import fetch, { Response } from 'node-fetch';
|
||||
import * as unzipper from 'unzipper';
|
||||
import { zip } from 'zip-a-folder';
|
||||
import {
|
||||
Uri,
|
||||
ProgressOptions,
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
} from 'vscode';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
|
||||
import { DatabaseManager, DatabaseItem } from './databases';
|
||||
import {
|
||||
ProgressCallback,
|
||||
@@ -183,9 +185,9 @@ async function databaseArchiveFetcher(
|
||||
progressCallback?: ProgressCallback
|
||||
): Promise<DatabaseItem> {
|
||||
progressCallback?.({
|
||||
maxStep: 3,
|
||||
message: 'Getting database',
|
||||
step: 1,
|
||||
maxStep: 4,
|
||||
});
|
||||
if (!storagePath) {
|
||||
throw new Error('No storage path specified.');
|
||||
@@ -200,9 +202,9 @@ async function databaseArchiveFetcher(
|
||||
}
|
||||
|
||||
progressCallback?.({
|
||||
maxStep: 3,
|
||||
message: 'Opening database',
|
||||
step: 3,
|
||||
maxStep: 4,
|
||||
});
|
||||
|
||||
// find the path to the database. The actual database might be in a sub-folder
|
||||
@@ -212,6 +214,13 @@ async function databaseArchiveFetcher(
|
||||
'codeql-database.yml'
|
||||
);
|
||||
if (dbPath) {
|
||||
progressCallback?.({
|
||||
message: 'Validating and fixing source location',
|
||||
step: 4,
|
||||
maxStep: 4,
|
||||
});
|
||||
await ensureZippedSourceLocation(dbPath);
|
||||
|
||||
const item = await databasesManager.openDatabase(Uri.file(dbPath));
|
||||
databasesManager.setCurrentDatabaseItem(item);
|
||||
return item;
|
||||
@@ -261,19 +270,9 @@ function validateHttpsUrl(databaseUrl: string) {
|
||||
}
|
||||
|
||||
async function readAndUnzip(databaseUrl: string, unzipPath: string) {
|
||||
const unzipStream = unzipper.Extract({
|
||||
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.pipe(unzipStream);
|
||||
});
|
||||
const databaseFile = Uri.parse(databaseUrl).fsPath;
|
||||
const directory = await unzipper.Open.file(databaseFile);
|
||||
await directory.extract({ path: unzipPath });
|
||||
}
|
||||
|
||||
async function fetchAndUnzip(
|
||||
@@ -288,6 +287,7 @@ async function fetchAndUnzip(
|
||||
const unzipStream = unzipper.Extract({
|
||||
path: unzipPath,
|
||||
});
|
||||
|
||||
progressCallback?.({
|
||||
maxStep: 3,
|
||||
message: 'Unzipping database',
|
||||
@@ -444,3 +444,24 @@ async function promptForLanguage(
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Databases created by the old odasa tool will not have a zipped
|
||||
* source location. However, this extension works better if sources
|
||||
* are zipped.
|
||||
*
|
||||
* This function ensures that the source location is zipped. If the
|
||||
* `src` folder exists and the `src.zip` file does not, the `src`
|
||||
* folder will be zipped and then deleted.
|
||||
*
|
||||
* @param databasePath The full path to the unzipped database
|
||||
*/
|
||||
async function ensureZippedSourceLocation(databasePath: string): Promise<void> {
|
||||
const srcFolderPath = path.join(databasePath, 'src');
|
||||
const srcZipPath = srcFolderPath + '.zip';
|
||||
|
||||
if ((await fs.pathExists(srcFolderPath)) && !(await fs.pathExists(srcZipPath))) {
|
||||
await zip(srcFolderPath, srcZipPath);
|
||||
await fs.remove(srcFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,7 +594,8 @@ export class DatabaseUI extends DisposableObject {
|
||||
if ((await fs.stat(dbPath)).isFile()) {
|
||||
dbPath = path.dirname(dbPath);
|
||||
}
|
||||
if (path.basename(dbPath).startsWith('db-')) {
|
||||
|
||||
if (isLikelyDbFolder(dbPath)) {
|
||||
dbPath = path.dirname(dbPath);
|
||||
}
|
||||
return Uri.file(dbPath);
|
||||
@@ -609,3 +610,8 @@ export class DatabaseUI extends DisposableObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const dbRegeEx = /^db-(javascript|go|cpp|java|python)$/;
|
||||
function isLikelyDbFolder(dbPath: string) {
|
||||
return path.basename(dbPath).match(dbRegeEx);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ async function findSourceArchive(
|
||||
|
||||
async function resolveDatabase(
|
||||
databasePath: string
|
||||
): Promise<DatabaseContents | undefined> {
|
||||
): Promise<DatabaseContents> {
|
||||
|
||||
const name = path.basename(databasePath);
|
||||
|
||||
@@ -155,20 +155,6 @@ async function getDbSchemeFiles(dbDirectory: string): Promise<string[]> {
|
||||
return await glob('*.dbscheme', { cwd: dbDirectory });
|
||||
}
|
||||
|
||||
async function resolveRawDataset(datasetPath: string): Promise<DatabaseContents | undefined> {
|
||||
if ((await getDbSchemeFiles(datasetPath)).length > 0) {
|
||||
return {
|
||||
kind: DatabaseKind.RawDataset,
|
||||
name: path.basename(datasetPath),
|
||||
datasetUri: vscode.Uri.file(datasetPath),
|
||||
sourceArchiveUri: undefined
|
||||
};
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveDatabaseContents(uri: vscode.Uri): Promise<DatabaseContents> {
|
||||
if (uri.scheme !== 'file') {
|
||||
throw new Error(`Database URI scheme '${uri.scheme}' not supported; only 'file' URIs are supported.`);
|
||||
@@ -178,7 +164,7 @@ async function resolveDatabaseContents(uri: vscode.Uri): Promise<DatabaseContent
|
||||
throw new InvalidDatabaseError(`Database '${databasePath}' does not exist.`);
|
||||
}
|
||||
|
||||
const contents = await resolveDatabase(databasePath) || await resolveRawDataset(databasePath);
|
||||
const contents = await resolveDatabase(databasePath);
|
||||
|
||||
if (contents === undefined) {
|
||||
throw new InvalidDatabaseError(`'${databasePath}' is not a valid database.`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as tmp from 'tmp';
|
||||
import * as tmp from 'tmp-promise';
|
||||
import * as vscode from 'vscode';
|
||||
import { decodeSourceArchiveUri, zipArchiveScheme } from './archive-filesystem-provider';
|
||||
import { ColumnKindCode, EntityValue, getResultSetSchema, LineColumnLocation, UrlValue } from './bqrs-cli-types';
|
||||
@@ -42,7 +42,9 @@ function nameOfKeyType(keyType: KeyType): string {
|
||||
}
|
||||
|
||||
async function resolveQueries(cli: CodeQLCliServer, qlpack: string, keyType: KeyType): Promise<string[]> {
|
||||
const suiteFile = tmp.fileSync({ postfix: '.qls' }).name;
|
||||
const suiteFile = (await tmp.file({
|
||||
postfix: '.qls'
|
||||
})).path;
|
||||
const suiteYaml = { qlpack, include: { kind: 'definitions', 'tags contain': tagOfKeyType(keyType) } };
|
||||
await fs.writeFile(suiteFile, yaml.safeDump(suiteYaml), 'utf8');
|
||||
|
||||
|
||||
@@ -23,11 +23,6 @@ export type PathTableResultSet = {
|
||||
|
||||
export type ResultSet = RawTableResultSet | PathTableResultSet;
|
||||
|
||||
/**
|
||||
* Only ever show this many results per run in interpreted results.
|
||||
*/
|
||||
export const INTERPRETED_RESULTS_PER_RUN_LIMIT = 100;
|
||||
|
||||
/**
|
||||
* Only ever show this many rows in a raw result table.
|
||||
*/
|
||||
@@ -38,6 +33,11 @@ export const RAW_RESULTS_LIMIT = 10000;
|
||||
*/
|
||||
export const RAW_RESULTS_PAGE_SIZE = 100;
|
||||
|
||||
/**
|
||||
* Show this many rows in an interpreted results table at a time.
|
||||
*/
|
||||
export const INTERPRETED_RESULTS_PAGE_SIZE = 100;
|
||||
|
||||
export interface DatabaseInfo {
|
||||
name: string;
|
||||
databaseUri: string;
|
||||
@@ -61,6 +61,7 @@ export interface PreviousExecution {
|
||||
export interface Interpretation {
|
||||
sourceLocationPrefix: string;
|
||||
numTruncatedResults: number;
|
||||
numTotalResults: number;
|
||||
/**
|
||||
* sortState being undefined means don't sort, just present results in the order
|
||||
* they appear in the sarif file.
|
||||
@@ -113,6 +114,16 @@ export interface SetStateMsg {
|
||||
parsedResultSets: ParsedResultSets;
|
||||
}
|
||||
|
||||
export interface ShowInterpretedPageMsg {
|
||||
t: 'showInterpretedPage';
|
||||
interpretation: Interpretation;
|
||||
database: DatabaseInfo;
|
||||
metadata?: QueryMetadata;
|
||||
pageNumber: number;
|
||||
numPages: number;
|
||||
resultSetNames: string[];
|
||||
}
|
||||
|
||||
/** Advance to the next or previous path no in the path viewer */
|
||||
export interface NavigatePathMsg {
|
||||
t: 'navigatePath';
|
||||
@@ -124,6 +135,7 @@ export interface NavigatePathMsg {
|
||||
export type IntoResultsViewMsg =
|
||||
| ResultsUpdatingMsg
|
||||
| SetStateMsg
|
||||
| ShowInterpretedPageMsg
|
||||
| NavigatePathMsg;
|
||||
|
||||
export type FromResultsViewMsg =
|
||||
|
||||
@@ -19,7 +19,6 @@ import { assertNever } from './helpers-pure';
|
||||
import {
|
||||
FromResultsViewMsg,
|
||||
Interpretation,
|
||||
INTERPRETED_RESULTS_PER_RUN_LIMIT,
|
||||
IntoResultsViewMsg,
|
||||
QueryMetadata,
|
||||
ResultsPaths,
|
||||
@@ -28,6 +27,8 @@ import {
|
||||
InterpretedResultsSortState,
|
||||
SortDirection,
|
||||
RAW_RESULTS_PAGE_SIZE,
|
||||
INTERPRETED_RESULTS_PAGE_SIZE,
|
||||
ALERTS_TABLE_NAME,
|
||||
} from './interface-types';
|
||||
import { Logger } from './logging';
|
||||
import * as messages from './messages';
|
||||
@@ -51,6 +52,7 @@ import {
|
||||
jumpToLocation,
|
||||
} from './interface-utils';
|
||||
import { getDefaultResultSetName } from './interface-types';
|
||||
import { ResultSetSchema } from './bqrs-cli-types';
|
||||
|
||||
/**
|
||||
* interface.ts
|
||||
@@ -95,6 +97,10 @@ function numPagesOfResultSet(resultSet: RawResultSet): number {
|
||||
return Math.ceil(resultSet.schema.tupleCount / RAW_RESULTS_PAGE_SIZE);
|
||||
}
|
||||
|
||||
function numInterpretedPages(interpretation: Interpretation | undefined): number {
|
||||
return Math.ceil((interpretation?.sarif.runs[0].results?.length || 0) / INTERPRETED_RESULTS_PAGE_SIZE);
|
||||
}
|
||||
|
||||
export class InterfaceManager extends DisposableObject {
|
||||
private _displayedQuery?: CompletedQuery;
|
||||
private _interpretation?: Interpretation;
|
||||
@@ -244,7 +250,12 @@ export class InterfaceManager extends DisposableObject {
|
||||
);
|
||||
break;
|
||||
case 'changePage':
|
||||
await this.showPageOfResults(msg.selectedTable, msg.pageNumber);
|
||||
if (msg.selectedTable === ALERTS_TABLE_NAME) {
|
||||
await this.showPageOfInterpretedResults(msg.pageNumber);
|
||||
}
|
||||
else {
|
||||
await this.showPageOfRawResults(msg.selectedTable, msg.pageNumber);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assertNever(msg);
|
||||
@@ -283,7 +294,8 @@ export class InterfaceManager extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
const interpretation = await this.interpretResultsInfo(
|
||||
this._interpretation = undefined;
|
||||
const interpretationPage = await this.interpretResultsInfo(
|
||||
results.query,
|
||||
results.interpretedResultsSortState
|
||||
);
|
||||
@@ -295,7 +307,6 @@ export class InterfaceManager extends DisposableObject {
|
||||
);
|
||||
|
||||
this._displayedQuery = results;
|
||||
this._interpretation = interpretation;
|
||||
|
||||
const panel = this.getPanel();
|
||||
await this.waitForPanelLoaded();
|
||||
@@ -325,19 +336,13 @@ export class InterfaceManager extends DisposableObject {
|
||||
|
||||
const getParsedResultSets = async (): Promise<ParsedResultSets> => {
|
||||
if (EXPERIMENTAL_BQRS_SETTING.getValue()) {
|
||||
const schemas = await this.cliServer.bqrsInfo(
|
||||
results.query.resultsPaths.resultsPath,
|
||||
RAW_RESULTS_PAGE_SIZE
|
||||
);
|
||||
|
||||
const resultSetNames = schemas['result-sets'].map(
|
||||
(resultSet) => resultSet.name
|
||||
);
|
||||
const resultSetSchemas = await this.getResultSetSchemas(results);
|
||||
const resultSetNames = resultSetSchemas.map(schema => schema.name);
|
||||
|
||||
// This may not wind up being the page we actually show, if there are interpreted results,
|
||||
// but speculatively send it anyway.
|
||||
const selectedTable = getDefaultResultSetName(resultSetNames);
|
||||
const schema = schemas['result-sets'].find(
|
||||
const schema = resultSetSchemas.find(
|
||||
(resultSet) => resultSet.name == selectedTable
|
||||
)!;
|
||||
if (schema === undefined) {
|
||||
@@ -352,12 +357,12 @@ export class InterfaceManager extends DisposableObject {
|
||||
);
|
||||
const adaptedSchema = adaptSchema(schema);
|
||||
const resultSet = adaptBqrs(adaptedSchema, chunk);
|
||||
|
||||
return {
|
||||
t: 'ExtensionParsed',
|
||||
pageNumber: 0,
|
||||
numPages: numPagesOfResultSet(resultSet),
|
||||
resultSet,
|
||||
numInterpretedPages: numInterpretedPages(this._interpretation),
|
||||
resultSet: { t: 'RawResultSet', ...resultSet },
|
||||
selectedTable: undefined,
|
||||
resultSetNames,
|
||||
};
|
||||
@@ -368,7 +373,7 @@ export class InterfaceManager extends DisposableObject {
|
||||
|
||||
await this.postMessage({
|
||||
t: 'setState',
|
||||
interpretation,
|
||||
interpretation: interpretationPage,
|
||||
origResultsPaths: results.query.resultsPaths,
|
||||
resultsPath: this.convertPathToWebviewUri(
|
||||
results.query.resultsPaths.resultsPath
|
||||
@@ -381,10 +386,48 @@ export class InterfaceManager extends DisposableObject {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a page of interpreted results
|
||||
*/
|
||||
public async showPageOfInterpretedResults(
|
||||
pageNumber: number
|
||||
): Promise<void> {
|
||||
if (this._displayedQuery === undefined) {
|
||||
throw new Error('Trying to show interpreted results but displayed query was undefined');
|
||||
}
|
||||
if (this._interpretation === undefined) {
|
||||
throw new Error('Trying to show interpreted results but interpretation was undefined');
|
||||
}
|
||||
if (this._interpretation.sarif.runs[0].results === undefined) {
|
||||
throw new Error('Trying to show interpreted results but results were undefined');
|
||||
}
|
||||
|
||||
const resultSetSchemas = await this.getResultSetSchemas(this._displayedQuery);
|
||||
const resultSetNames = resultSetSchemas.map(schema => schema.name);
|
||||
|
||||
await this.postMessage({
|
||||
t: 'showInterpretedPage',
|
||||
interpretation: this.getPageOfInterpretedResults(pageNumber),
|
||||
database: this._displayedQuery.database,
|
||||
metadata: this._displayedQuery.query.metadata,
|
||||
pageNumber,
|
||||
resultSetNames,
|
||||
numPages: numInterpretedPages(this._interpretation),
|
||||
});
|
||||
}
|
||||
|
||||
private async getResultSetSchemas(results: CompletedQuery): Promise<ResultSetSchema[]> {
|
||||
const schemas = await this.cliServer.bqrsInfo(
|
||||
results.query.resultsPaths.resultsPath,
|
||||
RAW_RESULTS_PAGE_SIZE
|
||||
);
|
||||
return schemas['result-sets'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a page of raw results from the chosen table.
|
||||
*/
|
||||
public async showPageOfResults(
|
||||
public async showPageOfRawResults(
|
||||
selectedTable: string,
|
||||
pageNumber: number
|
||||
): Promise<void> {
|
||||
@@ -399,16 +442,10 @@ export class InterfaceManager extends DisposableObject {
|
||||
(sortedResultsMap[k] = this.convertPathPropertiesToWebviewUris(v))
|
||||
);
|
||||
|
||||
const schemas = await this.cliServer.bqrsInfo(
|
||||
results.query.resultsPaths.resultsPath,
|
||||
RAW_RESULTS_PAGE_SIZE
|
||||
);
|
||||
const resultSetSchemas = await this.getResultSetSchemas(results);
|
||||
const resultSetNames = resultSetSchemas.map(schema => schema.name);
|
||||
|
||||
const resultSetNames = schemas['result-sets'].map(
|
||||
(resultSet) => resultSet.name
|
||||
);
|
||||
|
||||
const schema = schemas['result-sets'].find(
|
||||
const schema = resultSetSchemas.find(
|
||||
(resultSet) => resultSet.name == selectedTable
|
||||
)!;
|
||||
if (schema === undefined)
|
||||
@@ -426,8 +463,9 @@ export class InterfaceManager extends DisposableObject {
|
||||
const parsedResultSets: ParsedResultSets = {
|
||||
t: 'ExtensionParsed',
|
||||
pageNumber,
|
||||
resultSet,
|
||||
resultSet: { t: 'RawResultSet', ...resultSet },
|
||||
numPages: numPagesOfResultSet(resultSet),
|
||||
numInterpretedPages: numInterpretedPages(this._interpretation),
|
||||
selectedTable: selectedTable,
|
||||
resultSetNames,
|
||||
};
|
||||
@@ -447,7 +485,7 @@ export class InterfaceManager extends DisposableObject {
|
||||
});
|
||||
}
|
||||
|
||||
private async getTruncatedResults(
|
||||
private async _getInterpretedResults(
|
||||
metadata: QueryMetadata | undefined,
|
||||
resultsPaths: ResultsPaths,
|
||||
sourceInfo: cli.SourceInfo | undefined,
|
||||
@@ -460,37 +498,58 @@ export class InterfaceManager extends DisposableObject {
|
||||
resultsPaths,
|
||||
sourceInfo
|
||||
);
|
||||
// For performance reasons, limit the number of results we try
|
||||
// to serialize and send to the webview. TODO: possibly also
|
||||
// limit number of paths per result, number of steps per path,
|
||||
// or throw an error if we are in aggregate trying to send
|
||||
// massively too much data, as it can make the extension
|
||||
// unresponsive.
|
||||
|
||||
let numTruncatedResults = 0;
|
||||
sarif.runs.forEach((run) => {
|
||||
if (run.results !== undefined) {
|
||||
sarif.runs.forEach(run => {
|
||||
if (run.results !== undefined)
|
||||
sortInterpretedResults(run.results, sortState);
|
||||
if (run.results.length > INTERPRETED_RESULTS_PER_RUN_LIMIT) {
|
||||
numTruncatedResults +=
|
||||
run.results.length - INTERPRETED_RESULTS_PER_RUN_LIMIT;
|
||||
run.results = run.results.slice(0, INTERPRETED_RESULTS_PER_RUN_LIMIT);
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
|
||||
const numTotalResults = (() => {
|
||||
if (sarif.runs.length === 0) return 0;
|
||||
if (sarif.runs[0].results === undefined) return 0;
|
||||
return sarif.runs[0].results.length;
|
||||
})();
|
||||
|
||||
const interpretation: Interpretation = {
|
||||
sarif,
|
||||
sourceLocationPrefix,
|
||||
numTruncatedResults,
|
||||
numTruncatedResults: 0,
|
||||
numTotalResults,
|
||||
sortState,
|
||||
};
|
||||
this._interpretation = interpretation;
|
||||
return interpretation;
|
||||
}
|
||||
|
||||
private getPageOfInterpretedResults(
|
||||
pageNumber: number
|
||||
): Interpretation {
|
||||
|
||||
function getPageOfRun(run: Sarif.Run): Sarif.Run {
|
||||
return {
|
||||
...run, results: run.results?.slice(
|
||||
INTERPRETED_RESULTS_PAGE_SIZE * pageNumber,
|
||||
INTERPRETED_RESULTS_PAGE_SIZE * (pageNumber + 1)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
if (this._interpretation === undefined) {
|
||||
throw new Error('Tried to get interpreted results before interpretation finished');
|
||||
}
|
||||
if (this._interpretation.sarif.runs.length !== 1) {
|
||||
this.logger.log(`Warning: SARIF file had ${this._interpretation.sarif.runs.length} runs, expected 1`);
|
||||
}
|
||||
const interp = this._interpretation;
|
||||
return {
|
||||
...interp,
|
||||
sarif: { ...interp.sarif, runs: [getPageOfRun(interp.sarif.runs[0])] },
|
||||
};
|
||||
}
|
||||
|
||||
private async interpretResultsInfo(
|
||||
query: QueryInfo,
|
||||
sortState: InterpretedResultsSortState | undefined
|
||||
): Promise<Interpretation | undefined> {
|
||||
let interpretation: Interpretation | undefined = undefined;
|
||||
if (
|
||||
(await query.canHaveInterpretedResults()) &&
|
||||
query.quickEvalPosition === undefined // never do results interpretation if quickEval
|
||||
@@ -507,7 +566,7 @@ export class InterfaceManager extends DisposableObject {
|
||||
sourceArchive: sourceArchiveUri.fsPath,
|
||||
sourceLocationPrefix,
|
||||
};
|
||||
interpretation = await this.getTruncatedResults(
|
||||
await this._getInterpretedResults(
|
||||
query.metadata,
|
||||
query.resultsPaths,
|
||||
sourceInfo,
|
||||
@@ -522,7 +581,7 @@ export class InterfaceManager extends DisposableObject {
|
||||
);
|
||||
}
|
||||
}
|
||||
return interpretation;
|
||||
return this._interpretation && this.getPageOfInterpretedResults(0);
|
||||
}
|
||||
|
||||
private async showResultsAsDiagnostics(
|
||||
@@ -541,7 +600,8 @@ export class InterfaceManager extends DisposableObject {
|
||||
sourceArchive: sourceArchiveUri.fsPath,
|
||||
sourceLocationPrefix,
|
||||
};
|
||||
const interpretation = await this.getTruncatedResults(
|
||||
// TODO: Performance-testing to determine whether this truncation is necessary.
|
||||
const interpretation = await this._getInterpretedResults(
|
||||
metadata,
|
||||
resultsInfo,
|
||||
sourceInfo,
|
||||
|
||||
@@ -167,7 +167,7 @@ export class QueryHistoryManager {
|
||||
treeDataProvider: HistoryTreeDataProvider;
|
||||
treeView: vscode.TreeView<CompletedQuery>;
|
||||
lastItemClick: { time: Date; item: CompletedQuery } | undefined;
|
||||
|
||||
compareWithItem: CompletedQuery | undefined;
|
||||
|
||||
constructor(
|
||||
ctx: ExtensionContext,
|
||||
@@ -185,6 +185,7 @@ export class QueryHistoryManager {
|
||||
treeDataProvider,
|
||||
canSelectMany: true,
|
||||
});
|
||||
|
||||
// Lazily update the tree view selection due to limitations of TreeView API (see
|
||||
// `updateTreeViewSelectionIfVisible` doc for details)
|
||||
this.treeView.onDidChangeVisibility(async (_ev) =>
|
||||
@@ -195,6 +196,7 @@ export class QueryHistoryManager {
|
||||
if (ev.selection.length == 0) {
|
||||
this.updateTreeViewSelectionIfVisible();
|
||||
}
|
||||
this.updateCompareWith(ev.selection);
|
||||
});
|
||||
logger.log('Registering query history panel commands.');
|
||||
ctx.subscriptions.push(
|
||||
@@ -349,8 +351,8 @@ export class QueryHistoryManager {
|
||||
throw new Error('Please select a successful query.');
|
||||
}
|
||||
|
||||
const from = singleItem;
|
||||
const to = await this.findOtherQueryToCompare(singleItem, multiSelect);
|
||||
const from = this.compareWithItem || singleItem;
|
||||
const to = await this.findOtherQueryToCompare(from, multiSelect);
|
||||
|
||||
if (from && to) {
|
||||
this.doCompareCallback(from, to);
|
||||
@@ -415,7 +417,7 @@ export class QueryHistoryManager {
|
||||
: singleItem.queryName + '.ql';
|
||||
const params = new URLSearchParams({
|
||||
isQuickEval: String(!!singleItem.query.quickEvalPosition),
|
||||
queryText: await this.getQueryText(singleItem),
|
||||
queryText: encodeURIComponent(await this.getQueryText(singleItem)),
|
||||
});
|
||||
const uri = vscode.Uri.parse(
|
||||
`codeql:${singleItem.query.queryID}-${queryName}?${params.toString()}`
|
||||
@@ -588,4 +590,33 @@ the file in the file explorer and dragging it into the workspace.`
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the compare with source query. This ensures that all compare command invocations
|
||||
* when exactly 2 queries are selected always have the proper _from_ query. Always use
|
||||
* compareWithItem as the _from_ query.
|
||||
*
|
||||
* The heuristic is this:
|
||||
*
|
||||
* 1. If selection is empty or has length > 2 delete compareWithItem.
|
||||
* 2. If selection is length 1, then set that item to compareWithItem.
|
||||
* 3. If selection is length 2, then make sure compareWithItem is one of the selected items
|
||||
* if not, then delete compareWithItem. If it is then, do nothing.
|
||||
*
|
||||
* This ensures that compareWithItem is always the first item selected if there are only
|
||||
* two selected items.
|
||||
*
|
||||
* @param newSelection the new selection after the most recent selection change
|
||||
*/
|
||||
private updateCompareWith(newSelection: CompletedQuery[]) {
|
||||
if (newSelection.length === 1) {
|
||||
this.compareWithItem = newSelection[0];
|
||||
} else if (
|
||||
newSelection.length !== 2 ||
|
||||
!this.compareWithItem ||
|
||||
!newSelection.includes(this.compareWithItem)
|
||||
) {
|
||||
this.compareWithItem = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,7 @@ function getResultCount(resultSet: ResultSet): number {
|
||||
case 'RawResultSet':
|
||||
return resultSet.schema.tupleCount;
|
||||
case 'SarifResultSet':
|
||||
if (resultSet.sarif.runs.length === 0) return 0;
|
||||
if (resultSet.sarif.runs[0].results === undefined) return 0;
|
||||
return resultSet.sarif.runs[0].results.length + resultSet.numTruncatedResults;
|
||||
return resultSet.numTotalResults;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,22 +108,11 @@ export class ResultTables
|
||||
return this.props.parsedResultSets.t === 'ExtensionParsed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we actually should show pagination interface right now. This is
|
||||
* still false for the time being when we're viewing alerts.
|
||||
*/
|
||||
paginationEnabled(): boolean {
|
||||
return this.paginationAllowed() &&
|
||||
this.props.parsedResultSets.selectedTable !== ALERTS_TABLE_NAME &&
|
||||
this.state.selectedTable !== ALERTS_TABLE_NAME;
|
||||
}
|
||||
|
||||
constructor(props: ResultTablesProps) {
|
||||
super(props);
|
||||
|
||||
const selectedTable = props.parsedResultSets.selectedTable || getDefaultResultSet(this.getResultSets());
|
||||
|
||||
let selectedPage: string;
|
||||
|
||||
switch (props.parsedResultSets.t) {
|
||||
case 'ExtensionParsed':
|
||||
selectedPage = (props.parsedResultSets.pageNumber + 1) + '';
|
||||
@@ -134,15 +121,13 @@ export class ResultTables
|
||||
selectedPage = '';
|
||||
break;
|
||||
}
|
||||
|
||||
this.state = { selectedTable, selectedPage };
|
||||
}
|
||||
|
||||
private onTableSelectionChange = (event: React.ChangeEvent<HTMLSelectElement>): void => {
|
||||
const selectedTable = event.target.value;
|
||||
const fetchPageFromExtension = this.paginationAllowed() && selectedTable !== ALERTS_TABLE_NAME;
|
||||
|
||||
if (fetchPageFromExtension) {
|
||||
if (this.paginationAllowed()) {
|
||||
vscode.postMessage({
|
||||
t: 'changePage',
|
||||
pageNumber: 0,
|
||||
@@ -189,13 +174,22 @@ export class ResultTables
|
||||
|
||||
renderPageButtons(resultSets: ExtensionParsedResultSets): JSX.Element {
|
||||
const selectedTable = this.state.selectedTable;
|
||||
|
||||
// FIXME: The extension, not the view, should be in charge of deciding whether to initially show
|
||||
// a raw or alerts page. We have to conditionally recompute the number of pages here, because
|
||||
// on initial load of query results, resultSets.numPages will have the number of *raw* pages available,
|
||||
// not interpreted pages, because the extension doesn't know the view will default to showing alerts
|
||||
// instead.
|
||||
const numPages = selectedTable == ALERTS_TABLE_NAME ?
|
||||
resultSets.numInterpretedPages : resultSets.numPages;
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ selectedPage: e.target.value });
|
||||
};
|
||||
const choosePage = (input: string) => {
|
||||
const pageNumber = parseInt(input);
|
||||
if (pageNumber !== undefined && !isNaN(pageNumber)) {
|
||||
const actualPageNumber = Math.max(0, Math.min(pageNumber - 1, resultSets.numPages - 1));
|
||||
const actualPageNumber = Math.max(0, Math.min(pageNumber - 1, numPages - 1));
|
||||
vscode.postMessage({
|
||||
t: 'changePage',
|
||||
pageNumber: actualPageNumber,
|
||||
@@ -214,22 +208,30 @@ export class ResultTables
|
||||
const nextPage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
vscode.postMessage({
|
||||
t: 'changePage',
|
||||
pageNumber: Math.min(resultSets.pageNumber + 1, resultSets.numPages - 1),
|
||||
pageNumber: Math.min(resultSets.pageNumber + 1, numPages - 1),
|
||||
selectedTable,
|
||||
});
|
||||
};
|
||||
return <span>
|
||||
<button onClick={prevPage} ><</button>
|
||||
<input value={this.state.selectedPage} onChange={onChange}
|
||||
|
||||
return <span className="vscode-codeql__table-selection-header">
|
||||
<button onClick={prevPage} >«</button>
|
||||
<input
|
||||
type="number"
|
||||
size={3}
|
||||
value={this.state.selectedPage}
|
||||
onChange={onChange}
|
||||
onBlur={e => choosePage(e.target.value)}
|
||||
onKeyDown={e => { if (e.keyCode === 13) choosePage((e.target as HTMLInputElement).value); }}
|
||||
/>
|
||||
<button value=">" onClick={nextPage} >></button>
|
||||
<span>
|
||||
/ {numPages}
|
||||
</span>
|
||||
<button value=">" onClick={nextPage} >»</button>
|
||||
</span>;
|
||||
}
|
||||
|
||||
renderButtons(): JSX.Element {
|
||||
if (this.props.parsedResultSets.t === 'ExtensionParsed' && this.paginationEnabled())
|
||||
if (this.props.parsedResultSets.t === 'ExtensionParsed' && this.paginationAllowed())
|
||||
return this.renderPageButtons(this.props.parsedResultSets);
|
||||
else
|
||||
return <span />;
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
NavigatePathMsg,
|
||||
QueryMetadata,
|
||||
ResultsPaths,
|
||||
ALERTS_TABLE_NAME,
|
||||
} from '../interface-types';
|
||||
import { EventHandlers as EventHandlerList } from './event-handler-list';
|
||||
import { ResultTables } from './result-tables';
|
||||
@@ -193,6 +194,32 @@ class App extends React.Component<{}, ResultsViewState> {
|
||||
metadata: msg.metadata,
|
||||
});
|
||||
|
||||
this.loadResults();
|
||||
break;
|
||||
case 'showInterpretedPage':
|
||||
this.updateStateWithNewResultsInfo({
|
||||
resultsPath: '', // FIXME: Not used for interpreted, refactor so this is not needed
|
||||
parsedResultSets: {
|
||||
t: 'ExtensionParsed',
|
||||
numPages: msg.numPages,
|
||||
numInterpretedPages: msg.numPages,
|
||||
resultSetNames: msg.resultSetNames,
|
||||
pageNumber: msg.pageNumber,
|
||||
resultSet: {
|
||||
t: 'SarifResultSet',
|
||||
name: ALERTS_TABLE_NAME,
|
||||
schema: { name: ALERTS_TABLE_NAME, version: 0, columns: [], tupleCount: 1 },
|
||||
...msg.interpretation,
|
||||
},
|
||||
selectedTable: ALERTS_TABLE_NAME,
|
||||
},
|
||||
origResultsPaths: undefined as any, // FIXME: Not used for interpreted, refactor so this is not needed
|
||||
sortedResultsMap: new Map(), // FIXME: Not used for interpreted, refactor so this is not needed
|
||||
database: msg.database,
|
||||
interpretation: msg.interpretation,
|
||||
shouldKeepOldResultsWhileRendering: true,
|
||||
metadata: msg.metadata,
|
||||
});
|
||||
this.loadResults();
|
||||
break;
|
||||
case 'resultsUpdating':
|
||||
@@ -342,8 +369,10 @@ class App extends React.Component<{}, ResultsViewState> {
|
||||
displayedResults.resultsInfo !== null
|
||||
) {
|
||||
const parsedResultSets = displayedResults.resultsInfo.parsedResultSets;
|
||||
const key = (parsedResultSets.t === 'ExtensionParsed' ? (parsedResultSets.selectedTable || '') + parsedResultSets.pageNumber : '');
|
||||
return (
|
||||
<ResultTables
|
||||
key={key}
|
||||
parsedResultSets={parsedResultSets}
|
||||
rawResultSets={displayedResults.results.resultSets}
|
||||
interpretation={
|
||||
|
||||
@@ -7,16 +7,47 @@
|
||||
.vscode-codeql__table-selection-header {
|
||||
display: flex;
|
||||
padding: 0.5em 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vscode-codeql__table-selection-header select {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.vscode-codeql__table-selection-header button {
|
||||
padding: 0.3rem;
|
||||
margin: 0.2rem;
|
||||
border-radius: 5px;
|
||||
color: var(--vscode-editor-foreground);
|
||||
background-color: var(--vscode-editorGutter-background);
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.vscode-codeql__table-selection-header button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.vscode-codeql__table-selection-header input[type="number"] {
|
||||
border-radius: 0;
|
||||
padding: 0.3rem;
|
||||
margin: 0.2rem;
|
||||
width: 2rem;
|
||||
border-radius: 0;
|
||||
color: var(--vscode-editor-foreground);
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--vscode-editor-foreground);
|
||||
background-color: var(--vscode-editorGutter-background);
|
||||
border-radius: 0;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.vscode-codeql__result-table-alert-extras {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
margin-left: auto;
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.vscode-codeql__result-table-toggle-diagnostics {
|
||||
@@ -34,11 +65,11 @@
|
||||
margin: 3px 3px 1px 13px;
|
||||
}
|
||||
|
||||
|
||||
.vscode-codeql__result-table th {
|
||||
border-top: 1px solid rgba(88, 96, 105, 0.25);
|
||||
border-bottom: 1px solid rgba(88, 96, 105, 0.25);
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
|
||||
sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
||||
background: rgba(225, 228, 232, 0.25);
|
||||
padding: 0.25em 0.5em;
|
||||
text-align: center;
|
||||
@@ -146,7 +177,7 @@ td.vscode-codeql__path-index-cell {
|
||||
|
||||
.octicon {
|
||||
fill: var(--vscode-editor-foreground);
|
||||
margin-top: .25em;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.octicon-light {
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('databases-ui', () => {
|
||||
|
||||
it('should choose parent direcory when db-* is selected', async () => {
|
||||
const dir = tmp.dirSync().name;
|
||||
const dbDir = path.join(dir, 'db-hucairz');
|
||||
const dbDir = path.join(dir, 'db-javascript');
|
||||
await fs.mkdirs(dbDir);
|
||||
|
||||
const uri = await fixDbUri(Uri.file(dbDir));
|
||||
@@ -34,7 +34,7 @@ describe('databases-ui', () => {
|
||||
|
||||
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 dbDir = path.join(dir, 'db-javascript');
|
||||
const file = path.join(dbDir, 'nested');
|
||||
await fs.mkdirs(dbDir);
|
||||
await fs.createFile(file);
|
||||
@@ -42,6 +42,18 @@ describe('databases-ui', () => {
|
||||
const uri = await fixDbUri(Uri.file(file));
|
||||
expect(uri.toString()).to.eq(Uri.file(dir).toString());
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle a parent whose name is db-*', async () => {
|
||||
// fixes https://github.com/github/vscode-codeql/issues/482
|
||||
const dir = tmp.dirSync().name;
|
||||
const parentDir = path.join(dir, 'db-hucairz');
|
||||
const dbDir = path.join(parentDir, 'db-javascript');
|
||||
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(parentDir).toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -169,6 +169,42 @@ describe('query-history', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateCompareWith', () => {
|
||||
it('should update compareWithItem when there is a single item', () => {
|
||||
const queryHistory = createMockQueryHistory([]);
|
||||
queryHistory.updateCompareWith(['a']);
|
||||
expect(queryHistory.compareWithItem).to.be.eq('a');
|
||||
});
|
||||
|
||||
it('should delete compareWithItem when there are 0 items', () => {
|
||||
const queryHistory = createMockQueryHistory([]);
|
||||
queryHistory.compareWithItem = 'a';
|
||||
queryHistory.updateCompareWith([]);
|
||||
expect(queryHistory.compareWithItem).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should delete compareWithItem when there are more than 2 items', () => {
|
||||
const queryHistory = createMockQueryHistory([]);
|
||||
queryHistory.compareWithItem = 'a';
|
||||
queryHistory.updateCompareWith(['a', 'b', 'c']);
|
||||
expect(queryHistory.compareWithItem).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should delete compareWithItem when there are 2 items and disjoint from compareWithItem', () => {
|
||||
const queryHistory = createMockQueryHistory([]);
|
||||
queryHistory.compareWithItem = 'a';
|
||||
queryHistory.updateCompareWith(['b', 'c']);
|
||||
expect(queryHistory.compareWithItem).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should do nothing when compareWithItem exists and exactly 2 items', () => {
|
||||
const queryHistory = createMockQueryHistory([]);
|
||||
queryHistory.compareWithItem = 'a';
|
||||
queryHistory.updateCompareWith(['a', 'b']);
|
||||
expect(queryHistory.compareWithItem).to.be.eq('a');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createMockQueryHistory(allHistory: {}[]) {
|
||||
@@ -177,6 +213,8 @@ function createMockQueryHistory(allHistory: {}[]) {
|
||||
findOtherQueryToCompare: (QueryHistoryManager.prototype as any).findOtherQueryToCompare,
|
||||
treeDataProvider: {
|
||||
allHistory
|
||||
}
|
||||
},
|
||||
updateCompareWith: (QueryHistoryManager.prototype as any).updateCompareWith,
|
||||
compareWithItem: undefined as undefined | string,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user