mirror of https://github.com/google/oss-fuzz.git
infra: add vscode extension poc (#10592)
Add initial layout or a vscode extension. Several commands included in the extension, including commands for - oss-fuzz initialization - creating new project integrations - generating coverage reports - building projects from arbitrary locations in the filesystem - reproducing crashes easily - instant CIFuzz integration - creating fuzzing templates for rapid prototyping - ... Many ideas can be put into the vscode extension, for example: - support some form of e.g. sync with introspector.oss-fuzz.com -- for example where the plugin will check "has progress been made relative to what is currently at oss-fuzz". We could extend `helper.py` with some form of command called "check_progress` which will run a small build pipeline + coverage and check if the coverage performs better than what is currently achieved. I think there's more to explore in this space. - connect to auto-fuzz: https://github.com/ossf/fuzz-introspector/tree/main/tools/auto-fuzz - in general improve the extension UI, as currently it's only based on commands. --------- Signed-off-by: David Korczynski <david@adalogics.com>
This commit is contained in:
parent
920bb40321
commit
07b80397c5
|
@ -1,4 +1,4 @@
|
|||
.vscode/
|
||||
/.vscode/
|
||||
*.pyc
|
||||
/build/
|
||||
*~
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
|
@ -0,0 +1 @@
|
|||
build/
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "./node_modules/gts/"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
out
|
||||
build
|
||||
dist
|
||||
node_modules
|
||||
.vscode-test/
|
||||
*.vsix
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
module.exports = {
|
||||
...require('gts/.prettierrc.json')
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
.vscode/**
|
||||
.vscode-test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
|
@ -0,0 +1,5 @@
|
|||
# OSS-Fuzz visual studio code extension
|
||||
|
||||
Use this extension to build, run and introspector fuzzers by way of the [OSS-Fuzz](https://github.com/google/oss-fuzz) infrastructure.
|
||||
|
||||
This extension also provides capabilities to augment the IDE with information from OSS-Fuzz cloud database, showing details given by [Fuzz Introspector](https://github.com/ossf/fuzz-introspector).
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,131 @@
|
|||
{
|
||||
"name": "oss-fuzz",
|
||||
"displayName": "OssFuzz",
|
||||
"description": "",
|
||||
"version": "0.0.2",
|
||||
"publisher": "OSS-Fuzz maintainers",
|
||||
"engines": {
|
||||
"vscode": "^1.76.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"extensionDependencies": [
|
||||
"mindaro-dev.file-downloader"
|
||||
],
|
||||
"activationEvents": [],
|
||||
"main": "./build/src/extension.js",
|
||||
"contributes": {
|
||||
"configurations": [
|
||||
{
|
||||
"id": "OSS-Fuzz-Base",
|
||||
"title": "Base path to OSS-Fuzz",
|
||||
"properties": {
|
||||
"oss-fuzz.OSS.Path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"viewsWelcome": [
|
||||
{
|
||||
"view": "workbench.explorer.emptyView",
|
||||
"contents": "You can have paragraphs of text here. You can have [links](https://code.visualstudio.com) to external sources or [internal commands](command:welcome-view-content-sample.hello).\nUse new lines to have new paragraphs.\nPlace a link alone in a paragraph to make it a button\n[Hello](command:welcome-view-content-sample.hello)"
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "oss-fuzz.SetUp",
|
||||
"title": "OSS-Fuzz: Set up OSS-Fuzz"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.RunFuzzer",
|
||||
"title": "OSS-Fuzz: Run fuzzer from OSS-Fuzz project"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.ListFuzzers",
|
||||
"title": "OSS-Fuzz: List all fuzzers in a given project"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.SetOSSFuzzPath",
|
||||
"title": "OSS-Fuzz: Set OSS-Fuzz path"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.RunIntrospector",
|
||||
"title": "OSS-Fuzz: Run Fuzz Introspector on a project"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.GetCodeCoverage",
|
||||
"title": "OSS-Fuzz: get code coverage from OSS-Fuzz storage."
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.ClearCodeCoverage",
|
||||
"title": "OSS-Fuzz: clear code coverage"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.CreateOSSFuzzSetup",
|
||||
"title": "OSS-Fuzz: Create template files for OSS-Fuzz"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.WSBuildFuzzers",
|
||||
"title": "OSS-Fuzz: Build fuzzers from workspace"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.SetupCIFuzz",
|
||||
"title": "OSS-Fuzz: Set up CIFuzz"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.testFuzzer",
|
||||
"title": "OSS-Fuzz: test specific fuzzer"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.testCodeCoverage",
|
||||
"title": "OSS-Fuzz: run end-to-end test of project and collect code coverage"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.Reproduce",
|
||||
"title": "OSS-Fuzz: reproduce a testcase"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.SetGlobalProjectName",
|
||||
"title": "OSS-Fuzz: sets the default project name in the session"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.Redo",
|
||||
"title": "OSS-Fuzz: Redo the exact same command as previously, with same input"
|
||||
},
|
||||
{
|
||||
"command": "oss-fuzz.Template",
|
||||
"title": "OSS-Fuzz: Template fuzzer creation"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"pretest": "npm run compile",
|
||||
"lint": "gts lint",
|
||||
"clean": "gts clean",
|
||||
"fix": "gts fix",
|
||||
"prepare": "npm run compile",
|
||||
"posttest": "npm run lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^14.11.2",
|
||||
"@types/vscode": "^1.76.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
||||
"@typescript-eslint/parser": "^5.53.0",
|
||||
"@vscode/test-electron": "^2.2.3",
|
||||
"eslint": "^8.34.0",
|
||||
"glob": "^8.1.0",
|
||||
"mocha": "^10.2.0",
|
||||
"typescript": "~4.7.0",
|
||||
"gts": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/vscode-file-downloader-api": "^1.0.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import {println} from './logger';
|
||||
|
||||
/**
|
||||
* Creates a CIFuzz template
|
||||
* @param language
|
||||
* @param projectName
|
||||
* @param secondToRun
|
||||
* @returns
|
||||
*/
|
||||
export function cifuzzGenerator(
|
||||
language: string,
|
||||
projectName: string,
|
||||
secondToRun: Number
|
||||
) {
|
||||
println('Exporting cifuzz logic ' + language);
|
||||
|
||||
const cifuzzTemplate = `name: CIFuzz
|
||||
on: [pull_request]
|
||||
permissions: {}
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: '${projectName}'
|
||||
language: ${language}
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: '${projectName}'
|
||||
language: ${language}
|
||||
fuzz-seconds: ${secondToRun}
|
||||
output-sarif: true
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
- name: Upload Sarif
|
||||
if: always() && steps.build.outcome == 'success'
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
# Path to SARIF file relative to the root of the repository
|
||||
sarif_file: cifuzz-sarif/results.sarif
|
||||
checkout_path: cifuzz-sarif`;
|
||||
|
||||
return cifuzzTemplate;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
export const commandHistory: any[] = [];
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import {println} from '../logger';
|
||||
import {commandHistory} from '../commandUtils';
|
||||
import {hasOssFuzzInWorkspace, getOssFuzzWorkspaceProjectName} from '../utils';
|
||||
import {buildFuzzersFromWorkspace} from '../ossfuzzWrappers';
|
||||
|
||||
export async function cmdInputCollectorBuildFuzzersFromWorkspace() {
|
||||
let ossFuzzProjectName = '';
|
||||
// First determine if we have a name in the workspace
|
||||
if (await hasOssFuzzInWorkspace()) {
|
||||
/**
|
||||
* The fuzzers are in the workspace, as opposed to e.g. the oss-fuzz dirctory.
|
||||
*/
|
||||
ossFuzzProjectName = await getOssFuzzWorkspaceProjectName();
|
||||
} else {
|
||||
// If we did not have that, ask the user.
|
||||
|
||||
const ossFuzzProjectNameInput = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'The OSS-Fuzz project name',
|
||||
});
|
||||
if (!ossFuzzProjectNameInput) {
|
||||
println('Did not get a ossFuzzTargetProject');
|
||||
return false;
|
||||
}
|
||||
ossFuzzProjectName = ossFuzzProjectNameInput.toString();
|
||||
}
|
||||
|
||||
// Create an history object
|
||||
const args = new Object({
|
||||
projectName: ossFuzzProjectName,
|
||||
sanitizer: '',
|
||||
toClean: false,
|
||||
});
|
||||
|
||||
const commandObject = new Object({
|
||||
commandType: 'oss-fuzz.WSBuildFuzzers',
|
||||
Arguments: args,
|
||||
dispatcherFunc: cmdDispatchBuildFuzzersFromWorkspace,
|
||||
});
|
||||
console.log('L1: ' + commandHistory.length);
|
||||
commandHistory.push(commandObject);
|
||||
|
||||
await cmdDispatchBuildFuzzersFromWorkspace(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function cmdDispatchBuildFuzzersFromWorkspace(args: any) {
|
||||
await buildFuzzersFromWorkspace(
|
||||
args.projectName,
|
||||
args.sanitizer,
|
||||
args.toClean
|
||||
);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import {setupProjectInitialFiles} from '../projectIntegrationHelper';
|
||||
|
||||
export async function createOssFuzzSetup() {
|
||||
await setupProjectInitialFiles();
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import {loadCoverageIntoWorkspace} from '../coverageHelper';
|
||||
import {println} from '../logger';
|
||||
import {getApi, FileDownloader} from '@microsoft/vscode-file-downloader-api';
|
||||
|
||||
/*
|
||||
* Displays code coverage from OSS-Fuzz.
|
||||
*
|
||||
* Downloads a code coverage report from the OSS-Fuzz online storage, and then overlays
|
||||
* the relevant source files with the coverage information.
|
||||
*/
|
||||
export async function displayCodeCoverageFromOssFuzz(
|
||||
context: vscode.ExtensionContext
|
||||
) {
|
||||
const projectName = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: "The project you'd like to get code coverage for.",
|
||||
});
|
||||
if (!projectName) {
|
||||
return;
|
||||
}
|
||||
println('Getting code coverage for ' + projectName);
|
||||
|
||||
const fileDownloader: FileDownloader = await getApi();
|
||||
|
||||
const currentDate = new Date();
|
||||
const yesterday = new Date(currentDate);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
|
||||
const day = yesterday.getDate();
|
||||
const month = yesterday.getMonth();
|
||||
const year = yesterday.getFullYear();
|
||||
|
||||
try {
|
||||
const codeCoverageFile: vscode.Uri = await fileDownloader.downloadFile(
|
||||
vscode.Uri.parse(
|
||||
'https://storage.googleapis.com/oss-fuzz-coverage/' +
|
||||
projectName +
|
||||
'/textcov_reports/' +
|
||||
year.toString() +
|
||||
month.toString() +
|
||||
day.toString() +
|
||||
'/all_cov.json'
|
||||
),
|
||||
'all_cov.json',
|
||||
context
|
||||
);
|
||||
await loadCoverageIntoWorkspace(context, codeCoverageFile);
|
||||
} catch (err) {
|
||||
println(
|
||||
'Could not get the URL. Currently, this feature is only supported for Python projects'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
const fs = require('fs');
|
||||
import {println} from '../logger';
|
||||
import {commandHistory} from '../commandUtils';
|
||||
import {buildFuzzersFromWorkspace, runFuzzerHandler} from '../ossfuzzWrappers';
|
||||
import {listFuzzersForProject, systemSync} from '../utils';
|
||||
import {loadCoverageIntoWorkspace} from '../coverageHelper';
|
||||
import {extensionConfig} from '../config';
|
||||
|
||||
/**
|
||||
* Performs the activities:
|
||||
* 1) Build a project using address sanitizer
|
||||
* 2) Run each fuzzer of the project, saving corpus
|
||||
* 3) Build project using coverage sanitizer
|
||||
* 4) Collect coverage
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
export async function runEndToEndAndGetCoverage(
|
||||
context: vscode.ExtensionContext
|
||||
) {
|
||||
println('Getting code coverage');
|
||||
const ossFuzzProjectNameInput = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'The OSS-Fuzz project name',
|
||||
});
|
||||
if (!ossFuzzProjectNameInput) {
|
||||
println('Did not get a ossFuzzTargetProject');
|
||||
return;
|
||||
}
|
||||
const secondsToRunEachFuzzer = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'Seconds to run each fuzzer',
|
||||
});
|
||||
if (!secondsToRunEachFuzzer) {
|
||||
println('Did not get number of seconds to run each fuzzer');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an history object
|
||||
const args = new Object({
|
||||
projectName: ossFuzzProjectNameInput.toString(),
|
||||
secondsToRun: secondsToRunEachFuzzer.toString(),
|
||||
vsContext: context,
|
||||
});
|
||||
|
||||
const commandObject = new Object({
|
||||
commandType: 'oss-fuzz.cmdDispatchEndToEndRun',
|
||||
Arguments: args,
|
||||
dispatcherFunc: cmdDispatchEndToEndRun,
|
||||
});
|
||||
console.log('L1: ' + commandHistory.length);
|
||||
commandHistory.push(commandObject);
|
||||
|
||||
await cmdDispatchEndToEndRun(args);
|
||||
return;
|
||||
}
|
||||
|
||||
async function cmdDispatchEndToEndRun(args: any) {
|
||||
await endToEndRun(args.projectName, args.secondsToRun, args.vsContext);
|
||||
return;
|
||||
}
|
||||
|
||||
async function endToEndRun(
|
||||
ossFuzzProjectNameInput: string,
|
||||
secondsToRunEachFuzzer: string,
|
||||
context: vscode.ExtensionContext
|
||||
) {
|
||||
vscode.window.showInformationMessage(
|
||||
'Building project: ' + ossFuzzProjectNameInput.toString()
|
||||
);
|
||||
await buildFuzzersFromWorkspace(ossFuzzProjectNameInput.toString(), '', true);
|
||||
println('Build projects');
|
||||
|
||||
// List all of the fuzzers in the project
|
||||
const fuzzersInProject = await listFuzzersForProject(
|
||||
ossFuzzProjectNameInput,
|
||||
extensionConfig.ossFuzzPepositoryWorkPath
|
||||
);
|
||||
|
||||
// Run all of the fuzzers in the project
|
||||
println('Fuzzers found in project: ' + fuzzersInProject.toString());
|
||||
println('Running each of the fuzzers to collect a corpus');
|
||||
for (const fuzzName of fuzzersInProject) {
|
||||
println('Running fuzzer: ' + fuzzName);
|
||||
// Corpus directory
|
||||
const fuzzerCorpusPath =
|
||||
extensionConfig.ossFuzzPepositoryWorkPath +
|
||||
'/build/corpus/' +
|
||||
ossFuzzProjectNameInput +
|
||||
'/' +
|
||||
fuzzName;
|
||||
|
||||
await systemSync('mkdir', ['-p', fuzzerCorpusPath]);
|
||||
|
||||
await runFuzzerHandler(
|
||||
ossFuzzProjectNameInput,
|
||||
fuzzName,
|
||||
secondsToRunEachFuzzer.toString(),
|
||||
fuzzerCorpusPath
|
||||
);
|
||||
}
|
||||
|
||||
// Build with code coverage
|
||||
println('Building project with coverage sanitizer');
|
||||
await buildFuzzersFromWorkspace(
|
||||
ossFuzzProjectNameInput.toString(),
|
||||
'coverage',
|
||||
true
|
||||
);
|
||||
|
||||
// Run coverage command
|
||||
println('Collecting code coverage');
|
||||
const args: Array<string> = [
|
||||
extensionConfig.ossFuzzPepositoryWorkPath + '/infra/helper.py',
|
||||
'coverage',
|
||||
'--port',
|
||||
'',
|
||||
'--no-corpus-download',
|
||||
ossFuzzProjectNameInput.toString(),
|
||||
];
|
||||
await systemSync('python3', args);
|
||||
|
||||
println('Load coverage report with the command:');
|
||||
println(
|
||||
'python3 -m http.server 8008 --directory /tmp/oss-fuzz/build/out/' +
|
||||
ossFuzzProjectNameInput.toString() +
|
||||
'/report/'
|
||||
);
|
||||
|
||||
println('Trying to load code coverage in IDE');
|
||||
const allCovPath =
|
||||
extensionConfig.ossFuzzPepositoryWorkPath +
|
||||
'/build/out/' +
|
||||
ossFuzzProjectNameInput.toString() +
|
||||
'/textcov_reports/all_cov.json';
|
||||
if (fs.existsSync(allCovPath)) {
|
||||
const generatedCodeCoverageFile = vscode.Uri.file(allCovPath);
|
||||
await loadCoverageIntoWorkspace(context, generatedCodeCoverageFile);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {listFuzzersForProject} from '../utils';
|
||||
import {println} from '../logger';
|
||||
import {extensionConfig} from '../config';
|
||||
|
||||
/**
|
||||
* Lists all the fuzzers for a project.
|
||||
*/
|
||||
export async function listFuzzersHandler() {
|
||||
// Lists all of the fuzzers from a project.
|
||||
const projectName = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'Type a project name',
|
||||
});
|
||||
if (!projectName) {
|
||||
console.log('Failed to get project name');
|
||||
return;
|
||||
}
|
||||
println('Listing fuzzers for project ' + projectName);
|
||||
|
||||
await listFuzzersForProject(
|
||||
projectName?.toString(),
|
||||
extensionConfig.ossFuzzPepositoryWorkPath
|
||||
);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import {commandHistory} from '../commandUtils';
|
||||
|
||||
/**
|
||||
* Rerun the latest command
|
||||
*/
|
||||
export async function cmdDispatcherRe() {
|
||||
if (commandHistory.length === 0) {
|
||||
console.log('command history is empty');
|
||||
return false;
|
||||
}
|
||||
|
||||
const commandObj: any = commandHistory[commandHistory.length - 1];
|
||||
|
||||
console.log('Redoing');
|
||||
console.log(commandObj.commandType);
|
||||
await commandObj.dispatcherFunc(commandObj.Arguments);
|
||||
//await commandObj.dispatcherFunc(commandObj.args);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
const fs = require('fs');
|
||||
import {println} from '../logger';
|
||||
import {commandHistory} from '../commandUtils';
|
||||
import {systemSyncLogIfFailure} from '../utils';
|
||||
import {buildFuzzersFromWorkspace} from '../ossfuzzWrappers';
|
||||
import {extensionConfig} from '../config';
|
||||
const readline = require('readline');
|
||||
|
||||
|
||||
export async function cmdInputCollectorReproduceTestcase() {
|
||||
// Runs a fuzzer from a given project.
|
||||
const crashFileInput = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'The ID of the testcase.',
|
||||
});
|
||||
if (!crashFileInput) {
|
||||
return;
|
||||
}
|
||||
// Create an history object and append it to the command history.
|
||||
const args = new Object({
|
||||
crashFile: crashFileInput.toString(),
|
||||
});
|
||||
|
||||
const commandObject = new Object({
|
||||
commandType: 'oss-fuzz.ReproduceFuzzer',
|
||||
Arguments: args,
|
||||
dispatcherFunc: cmdDispatchReproduceTestcase,
|
||||
});
|
||||
commandHistory.push(commandObject);
|
||||
|
||||
await cmdDispatchReproduceTestcase(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function cmdDispatchReproduceTestcase(args: any) {
|
||||
await reproduceTestcase(args.crashFile);
|
||||
}
|
||||
|
||||
export async function reproduceTestcase(crashInfoFileInput: string) {
|
||||
println('Reproducing testcase for ' + crashInfoFileInput);
|
||||
println('Checking directory: ' + extensionConfig.crashesDirectory);
|
||||
|
||||
const crashInfoFile =
|
||||
extensionConfig.crashesDirectory + '/' + crashInfoFileInput + '.info';
|
||||
println(crashInfoFile);
|
||||
try {
|
||||
if (fs.existsSync(crashInfoFile)) {
|
||||
println('File exists');
|
||||
} else {
|
||||
println('Crash file does not exist');
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point the file exists
|
||||
const r = readline.createInterface({
|
||||
input: fs.createReadStream(crashInfoFile),
|
||||
});
|
||||
|
||||
let targetProject = 'N/A';
|
||||
let targetFuzzer = 'N/A';
|
||||
// Logic for passing the file. This is based off of clusterfuzz monorail reports,
|
||||
// and the intention is the file needs to be a copy of:
|
||||
//
|
||||
// Project: project-name
|
||||
// Fuzzing Engine: libFuzzer
|
||||
// Fuzz Target: fuzzer-name
|
||||
//
|
||||
// Example:
|
||||
// The following URL: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=59747
|
||||
// has the bug information:
|
||||
// """
|
||||
// Project: my-fuzzing-project
|
||||
// Fuzzing Engine: libFuzzer
|
||||
// Fuzz Target: the-fuzzer-name-fuzz-parseXX
|
||||
// Job Type: libfuzzer_asan_my-fuzzing-project
|
||||
// Platform Id: linux
|
||||
// """
|
||||
// and a link to a reproducer test case:
|
||||
// https://oss-fuzz.com/download?testcase_id=5009071179431936
|
||||
// which, when accessed will download the file
|
||||
// clusterfuzz-testcase-minimized-flb-it-fuzz-config_map_fuzzer_OSSFUZZ-5009071179431936
|
||||
//
|
||||
// To enable reproducing of this issue we need to:
|
||||
// - 1) Download the crash file and place it in the directory given in config.ts
|
||||
// and "crashesDirectory" variable.
|
||||
// - 2) create a file "5009071179431936.info" and paste the information above
|
||||
// (Project:... Fuzz Target: ...) into the file. This information is
|
||||
// needed because we need to know project name and fuzzer name in order
|
||||
// to reproduce the crash.
|
||||
// - 3) the reproducer can now be reproduced using the reproduce command
|
||||
// with argument "5009071179431936" as argument.
|
||||
r.on('line', (text: string) => {
|
||||
println(text);
|
||||
if (text.startsWith('Project: ')) {
|
||||
println('Starts with project');
|
||||
println(text.split('Project: ').toString());
|
||||
targetProject = text.split('Project: ')[1];
|
||||
} else if (text.startsWith('Fuzzing Engine: ')) {
|
||||
println('Starts with fuzzing engine');
|
||||
} else if (text.startsWith('Fuzz Target:')) {
|
||||
println('Starts with Fuzz Target');
|
||||
targetFuzzer = text.split('Fuzz Target: ')[1];
|
||||
} else if (text.startsWith('Job Type:')) {
|
||||
println('Starts with Job Type');
|
||||
}
|
||||
});
|
||||
|
||||
r.on('close', async () => {
|
||||
println('Target project: ' + targetProject);
|
||||
println('Target fuzzer: ' + targetFuzzer);
|
||||
|
||||
// Build a fresh version of the project.
|
||||
const buildResult: boolean = await buildFuzzersFromWorkspace(
|
||||
targetProject,
|
||||
'',
|
||||
true
|
||||
);
|
||||
if (!buildResult) {
|
||||
println('Failed to build fuzzers');
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have a fresh build of the project, proceed to reproduce the testcase.
|
||||
const crashInputTestCase =
|
||||
extensionConfig.crashesDirectory +
|
||||
'/' +
|
||||
'clusterfuzz-testcase-minimized-' +
|
||||
targetFuzzer +
|
||||
'-' +
|
||||
crashInfoFileInput;
|
||||
// Run reproduce command against the target file
|
||||
// Build the fuzzers using OSS-Fuzz infrastructure.
|
||||
const cmdToExec = 'python3';
|
||||
const args = [
|
||||
extensionConfig.ossFuzzPepositoryWorkPath + '/infra/helper.py',
|
||||
'reproduce',
|
||||
targetProject,
|
||||
targetFuzzer,
|
||||
crashInputTestCase,
|
||||
];
|
||||
if (!(await systemSyncLogIfFailure(cmdToExec, args))) {
|
||||
println('Failed to reproduce testcase');
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import {println} from '../logger';
|
||||
import {commandHistory} from '../commandUtils';
|
||||
import {runFuzzerHandler} from '../ossfuzzWrappers';
|
||||
|
||||
export async function cmdInputCollectorRunSpecificFuzzer() {
|
||||
let projectNameArg = '';
|
||||
let fuzzerName = '';
|
||||
let secondsToRun = '';
|
||||
|
||||
// Runs a fuzzer from a given project.
|
||||
const projectNameFromPrompt = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'Type a project name',
|
||||
});
|
||||
if (!projectNameFromPrompt) {
|
||||
println('Failed to get project name');
|
||||
return;
|
||||
}
|
||||
projectNameArg = projectNameFromPrompt.toString();
|
||||
const fuzzerNameFromPrompt = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'Type a fuzzer name',
|
||||
});
|
||||
if (!fuzzerNameFromPrompt) {
|
||||
println('Failed to get fuzzer name');
|
||||
return;
|
||||
}
|
||||
fuzzerName = fuzzerNameFromPrompt.toString();
|
||||
const secondsToRunInp = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'Type the number of seconds to run the fuzzer',
|
||||
});
|
||||
if (!secondsToRunInp) {
|
||||
return;
|
||||
}
|
||||
secondsToRun = secondsToRunInp.toString();
|
||||
|
||||
// Create an history object
|
||||
const args = new Object({
|
||||
projectName: projectNameArg,
|
||||
fuzzerName: fuzzerName,
|
||||
secondsToRun: secondsToRun,
|
||||
fuzzerCorpusPath: '',
|
||||
});
|
||||
|
||||
const commandObject = new Object({
|
||||
commandType: 'oss-fuzz.RunFuzzer',
|
||||
Arguments: args,
|
||||
dispatcherFunc: cmdDispatchRunFuzzerHandler,
|
||||
});
|
||||
console.log('L1: ' + commandHistory.length);
|
||||
commandHistory.push(commandObject);
|
||||
|
||||
await cmdDispatchRunFuzzerHandler(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function cmdDispatchRunFuzzerHandler(args: any) {
|
||||
await runFuzzerHandler(
|
||||
args.projectName,
|
||||
args.fuzzerName,
|
||||
args.secondsToRun,
|
||||
args.fuzzerCorpusPath
|
||||
);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {println} from '../logger';
|
||||
import {extensionConfig} from '../config';
|
||||
|
||||
// Set the oss-fuzz path.
|
||||
export async function setOssFuzzPath() {
|
||||
println('Setting path');
|
||||
const newOssFuzzPath = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'Type path',
|
||||
});
|
||||
if (!newOssFuzzPath) {
|
||||
println('Failed getting path');
|
||||
return;
|
||||
}
|
||||
|
||||
const fpathh = vscode.Uri.file(newOssFuzzPath);
|
||||
let isValid = false;
|
||||
try {
|
||||
if (await vscode.workspace.fs.readDirectory(fpathh)) {
|
||||
println('Is a directory');
|
||||
const helperPathURI = vscode.Uri.file(
|
||||
newOssFuzzPath + '/infra/helper.py'
|
||||
);
|
||||
if (await vscode.workspace.fs.readFile(helperPathURI)) {
|
||||
println('Found helper file');
|
||||
isValid = true;
|
||||
}
|
||||
isValid = true;
|
||||
} else {
|
||||
isValid = false;
|
||||
}
|
||||
} catch {
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
extensionConfig.ossFuzzPepositoryWorkPath = newOssFuzzPath;
|
||||
} else {
|
||||
println('Not setting OSS-Fuzz path');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import {println} from '../logger';
|
||||
import {determineWorkspaceLanguage} from '../utils';
|
||||
import {cifuzzGenerator} from '../cifuzz';
|
||||
|
||||
export async function setupCIFuzzHandler() {
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
|
||||
/**
|
||||
* Go through GitHub workflows to find potential traces of CIFuzz
|
||||
*/
|
||||
const githubWorkflowsPath = vscode.Uri.file(wsPath + '/.github/workflows');
|
||||
try {
|
||||
await vscode.workspace.fs.readDirectory(githubWorkflowsPath);
|
||||
} catch {
|
||||
println('Did not find a workflows path.');
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const [name, type] of await vscode.workspace.fs.readDirectory(
|
||||
githubWorkflowsPath
|
||||
)) {
|
||||
// Skip directories.
|
||||
if (type === 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read the files.
|
||||
println('Is a file');
|
||||
const workflowFile = vscode.Uri.file(wsPath + '/.github/workflows/' + name);
|
||||
const doc = await vscode.workspace.openTextDocument(workflowFile);
|
||||
if (doc.getText().includes('cifuzz')) {
|
||||
println('Found existing CIFuzz, will not continue.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
println('Did not find CIFuzz, creating one.');
|
||||
const projectName = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'OSS-Fuzz project name',
|
||||
});
|
||||
if (!projectName) {
|
||||
println('Failed to get project name');
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is no CIFuzz found, so we create one.
|
||||
*/
|
||||
// Determine the language of the workspace.
|
||||
const targetLanguage = await determineWorkspaceLanguage();
|
||||
println('Target language: ' + targetLanguage);
|
||||
|
||||
// Generate a CIFuzz workflow text.
|
||||
const cifuzzWorkflowText = cifuzzGenerator(targetLanguage, projectName, 30);
|
||||
|
||||
// Create the CIFuzz .yml file and write the contents to it to path
|
||||
// .github/workflows/cifuzz.yml
|
||||
const cifuzzYml = vscode.Uri.file(wsPath + '/.github/workflows/cifuzz.yml');
|
||||
const wsedit = new vscode.WorkspaceEdit();
|
||||
wsedit.createFile(cifuzzYml, {ignoreIfExists: true});
|
||||
wsedit.insert(cifuzzYml, new vscode.Position(0, 0), cifuzzWorkflowText);
|
||||
vscode.workspace.applyEdit(wsedit);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import {println} from '../logger';
|
||||
import {extensionConfig} from '../config';
|
||||
import {isPathValidOssFuzzPath} from '../ossfuzzWrappers';
|
||||
import {systemSync} from '../utils';
|
||||
|
||||
/**
|
||||
* Function for setting up oss-fuzz. This clones the relevant directory
|
||||
* and sets the oss-fuzz variable accordingly.
|
||||
*/
|
||||
export async function setUpOssFuzzHandler() {
|
||||
println('Setting up oss-fuzz in /tmp/');
|
||||
|
||||
// First check if we already have an OSS-Fuzz path
|
||||
const tmpOssFuzzRepositoryPath = '/tmp/oss-fuzz';
|
||||
|
||||
if ((await isPathValidOssFuzzPath(tmpOssFuzzRepositoryPath)) === true) {
|
||||
println('OSS-Fuzz already exists in /tmp/oss-fuzz');
|
||||
extensionConfig.ossFuzzPepositoryWorkPath = tmpOssFuzzRepositoryPath;
|
||||
return;
|
||||
}
|
||||
|
||||
const cmdToExec = 'git';
|
||||
const args: Array<string> = [
|
||||
'clone',
|
||||
'https://github.com/google/oss-fuzz',
|
||||
tmpOssFuzzRepositoryPath,
|
||||
];
|
||||
const [res, output] = await systemSync(cmdToExec, args);
|
||||
if (res === false) {
|
||||
println('Failed to clone oss-fuzz');
|
||||
println(output);
|
||||
return;
|
||||
}
|
||||
println('Finished cloning oss-fuzz');
|
||||
|
||||
extensionConfig.ossFuzzPepositoryWorkPath = tmpOssFuzzRepositoryPath;
|
||||
}
|
|
@ -0,0 +1,357 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Command for generating template fuzzers. This is a short-cut for rapid
|
||||
* prototyping as well as an archive for inspiration.
|
||||
*/
|
||||
import * as vscode from 'vscode';
|
||||
import {println} from '../logger';
|
||||
|
||||
const cLangSimpleStringFuzzer = `#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
char *new_str = (char *)malloc(size+1);
|
||||
if (new_str == NULL){
|
||||
return 0;
|
||||
}
|
||||
memcpy(new_str, data, size);
|
||||
new_str[size] = '\\0';
|
||||
|
||||
// Insert fuzzer contents here
|
||||
// fuzz data in new_str
|
||||
|
||||
// end of fuzzer contents
|
||||
|
||||
free(new_str);
|
||||
return 0;
|
||||
}`;
|
||||
|
||||
const cLangFileInputFuzzer = `int
|
||||
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
char filename[256];
|
||||
sprintf(filename, "/tmp/libfuzzer.%d", getpid());
|
||||
|
||||
// Create a file on the filesystem with fuzzer data in it
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
if (!fp) {
|
||||
return 0;
|
||||
}
|
||||
fwrite(data, size, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
// Fuzzer logic here. Use the file as a source of data.
|
||||
|
||||
// Fuzzer logic end
|
||||
|
||||
// Clean up the file.
|
||||
unlink(filename);
|
||||
|
||||
return 0;
|
||||
}`;
|
||||
|
||||
const cLangBareTemplateFuzzer = `int
|
||||
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}`;
|
||||
|
||||
const cppLangBareTemplateFuzzer = `extern "C" int
|
||||
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}`;
|
||||
|
||||
const cppLangStdStringTemplateFuzzer = `extern "C" int
|
||||
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
std::string input(reinterpret_cast<const char*>(data), size);
|
||||
|
||||
return 0;
|
||||
}`;
|
||||
|
||||
const cppLangFDPTemplateFuzzer = `#include <fuzzer/FuzzedDataProvider.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
extern "C" int
|
||||
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
FuzzedDataProvider fdp(data, size);
|
||||
|
||||
// Extract higher level data types used for fuzzing, e.g.
|
||||
// int ran_int = fdp.ConsumeIntegralInRange<int>(1, 1024);
|
||||
// std::string s = fdp.ConsumeRandomLengthString();
|
||||
|
||||
return 0;
|
||||
}`;
|
||||
|
||||
const cppLangFileInputFuzzer = `extern "C" int
|
||||
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
char filename[256];
|
||||
sprintf(filename, "/tmp/libfuzzer.%d", getpid());
|
||||
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
if (!fp) {
|
||||
return 0;
|
||||
}
|
||||
fwrite(data, size, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
// Fuzzer logic here
|
||||
|
||||
// Fuzzer logic end
|
||||
|
||||
unlink(filename);
|
||||
}`;
|
||||
|
||||
const pythonLangBareTemplate = `import sys
|
||||
import atheris
|
||||
|
||||
|
||||
def TestOneInput(fuzz_bytes):
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
atheris.Setup(sys.argv, TestOneInput)
|
||||
atheris.Fuzz()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()`;
|
||||
|
||||
const pythonLangFileInputFuzzer = `import sys
|
||||
import atheris
|
||||
|
||||
@atheris.instrument_func
|
||||
def TestOneInput(data):
|
||||
# Write fuzz data to a file
|
||||
with open('/tmp/fuzz_input.b') as f:
|
||||
f.write(data)
|
||||
|
||||
# Use '/tmp/fuzz_input.b' as input to file handling logic.
|
||||
|
||||
|
||||
def main():
|
||||
atheris.instrument_all()
|
||||
atheris.Setup(sys.argv, TestOneInput)
|
||||
atheris.Fuzz()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()`;
|
||||
|
||||
const pythonLongFdpTemplate = `import sys
|
||||
import atheris
|
||||
|
||||
def TestOneInput(fuzz_bytes):
|
||||
fdp = atheris.FuzzedDataProvider(fuzz_bytes)
|
||||
return
|
||||
|
||||
def main():
|
||||
atheris.Setup(sys.argv, TestOneInput)
|
||||
atheris.Fuzz()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()`;
|
||||
|
||||
const javaLangBareTemplate = `import com.code_intelligence.jazzer.api.FuzzedDataProvider;
|
||||
public class SampleFuzzer {
|
||||
public static void fuzzerTestOneInput(FuzzedDataProvider fdp) {
|
||||
// Use fdp to create arbitrary types seeded with fuzz data
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* C templates
|
||||
*/
|
||||
async function cTemplates() {
|
||||
let template = '';
|
||||
const result = await vscode.window.showQuickPick(
|
||||
['Bare template', 'Null-terminated string input', 'File input'],
|
||||
{
|
||||
placeHolder: 'Pick which template',
|
||||
}
|
||||
);
|
||||
vscode.window.showInformationMessage(`Got: ${result}`);
|
||||
|
||||
if (result === 'Null-terminated string input') {
|
||||
template = cLangSimpleStringFuzzer;
|
||||
} else if (result === 'File input') {
|
||||
template = cLangFileInputFuzzer;
|
||||
} else if (result === 'Bare template') {
|
||||
template = cLangBareTemplateFuzzer;
|
||||
} else {
|
||||
template = 'empty';
|
||||
}
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
|
||||
const cifuzzYml = vscode.Uri.file(wsPath + '/oss-fuzz-template.c');
|
||||
const wsedit = new vscode.WorkspaceEdit();
|
||||
wsedit.createFile(cifuzzYml, {ignoreIfExists: true});
|
||||
wsedit.insert(cifuzzYml, new vscode.Position(0, 0), template);
|
||||
vscode.workspace.applyEdit(wsedit);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* CPP templates
|
||||
*/
|
||||
async function cppTemplates() {
|
||||
let template = '';
|
||||
const result = await vscode.window.showQuickPick(
|
||||
[
|
||||
'Bare template',
|
||||
'Simple CPP string',
|
||||
'File input fuzzer',
|
||||
'Fuzzed data provider',
|
||||
],
|
||||
{
|
||||
placeHolder: 'Pick which template',
|
||||
}
|
||||
);
|
||||
vscode.window.showInformationMessage(`Got: ${result}`);
|
||||
|
||||
if (result === 'Bare template') {
|
||||
template = cppLangBareTemplateFuzzer;
|
||||
} else if (result === 'Simple CPP string') {
|
||||
template = cppLangStdStringTemplateFuzzer;
|
||||
} else if (result === 'File input fuzzer') {
|
||||
template = cppLangFileInputFuzzer;
|
||||
} else if (result === 'Fuzzed data provider') {
|
||||
template = cppLangFDPTemplateFuzzer;
|
||||
} else {
|
||||
template = 'empty';
|
||||
}
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
|
||||
const cifuzzYml = vscode.Uri.file(wsPath + '/oss-fuzz-template.cpp');
|
||||
const wsedit = new vscode.WorkspaceEdit();
|
||||
wsedit.createFile(cifuzzYml, {ignoreIfExists: true});
|
||||
wsedit.insert(cifuzzYml, new vscode.Position(0, 0), template);
|
||||
vscode.workspace.applyEdit(wsedit);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Python templates
|
||||
*/
|
||||
async function pythonTepmlates() {
|
||||
let template = '';
|
||||
const result = await vscode.window.showQuickPick(
|
||||
['Bare template', 'Fuzzed Data Provider', 'File input fuzzer'],
|
||||
{
|
||||
placeHolder: 'Pick which template',
|
||||
}
|
||||
);
|
||||
vscode.window.showInformationMessage(`Got: ${result}`);
|
||||
|
||||
if (result === 'Fuzzed Data Provider') {
|
||||
template = pythonLongFdpTemplate;
|
||||
} else if (result === 'Bare template') {
|
||||
template = pythonLangBareTemplate;
|
||||
} else if (result === 'File input fuzzer') {
|
||||
template = pythonLangFileInputFuzzer;
|
||||
} else {
|
||||
template = 'empty';
|
||||
}
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
|
||||
const cifuzzYml = vscode.Uri.file(wsPath + '/oss-fuzz-template.py');
|
||||
const wsedit = new vscode.WorkspaceEdit();
|
||||
wsedit.createFile(cifuzzYml, {ignoreIfExists: true});
|
||||
wsedit.insert(cifuzzYml, new vscode.Position(0, 0), template);
|
||||
vscode.workspace.applyEdit(wsedit);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Java templates
|
||||
*/
|
||||
async function javaTemplates() {
|
||||
let template = '';
|
||||
const result = await vscode.window.showQuickPick(['Bare template'], {
|
||||
placeHolder: 'Pick which template',
|
||||
});
|
||||
vscode.window.showInformationMessage(`Got: ${result}`);
|
||||
|
||||
if (result === 'Bare template') {
|
||||
template = javaLangBareTemplate;
|
||||
} else {
|
||||
template = 'empty';
|
||||
}
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
|
||||
const cifuzzYml = vscode.Uri.file(wsPath + '/oss-fuzz-template.java');
|
||||
const wsedit = new vscode.WorkspaceEdit();
|
||||
wsedit.createFile(cifuzzYml, {ignoreIfExists: true});
|
||||
wsedit.insert(cifuzzYml, new vscode.Position(0, 0), template);
|
||||
vscode.workspace.applyEdit(wsedit);
|
||||
return;
|
||||
}
|
||||
|
||||
export async function cmdDispatcherTemplate(context: vscode.ExtensionContext) {
|
||||
println('Creating template');
|
||||
const options: {
|
||||
[key: string]: (context: vscode.ExtensionContext) => Promise<void>;
|
||||
} = {
|
||||
C: cTemplates,
|
||||
CPP: cppTemplates,
|
||||
Python: pythonTepmlates,
|
||||
Java: javaTemplates,
|
||||
};
|
||||
|
||||
const quickPick = vscode.window.createQuickPick();
|
||||
quickPick.items = Object.keys(options).map(label => ({label}));
|
||||
quickPick.onDidChangeSelection(selection => {
|
||||
if (selection[0]) {
|
||||
options[selection[0].label](context).catch(console.error);
|
||||
}
|
||||
});
|
||||
quickPick.onDidHide(() => quickPick.dispose());
|
||||
quickPick.placeholder = 'Pick language';
|
||||
quickPick.show();
|
||||
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import {println} from '../logger';
|
||||
import {runFuzzerHandler, buildFuzzersFromWorkspace} from '../ossfuzzWrappers';
|
||||
import {commandHistory} from '../commandUtils';
|
||||
import {extensionConfig} from '../config';
|
||||
|
||||
/**
|
||||
* Does an end-to-end test of a project/fuzzer. This is done by
|
||||
* first building the project and then running the fuzzer.
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
|
||||
export async function cmdInputCollectorTestFuzzer() {
|
||||
// Get the project name and fuzzer name to test.
|
||||
const ossFuzzProjectNameInput = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'The OSS-Fuzz project name',
|
||||
});
|
||||
if (!ossFuzzProjectNameInput) {
|
||||
println('Did not get a ossFuzzTargetProject');
|
||||
return;
|
||||
}
|
||||
println('Project name: ' + ossFuzzProjectNameInput);
|
||||
|
||||
// Get the fuzzer to run
|
||||
const fuzzerNameInput = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'Type a fuzzer name',
|
||||
});
|
||||
if (!fuzzerNameInput) {
|
||||
println('Failed to get fuzzer name');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the args object for the dispatcher
|
||||
const args = new Object({
|
||||
projectName: ossFuzzProjectNameInput.toString(),
|
||||
fuzzerName: fuzzerNameInput.toString(),
|
||||
});
|
||||
|
||||
// Create a dispatcher object.
|
||||
const commandObject = new Object({
|
||||
commandType: 'oss-fuzz.TestFuzzer',
|
||||
Arguments: args,
|
||||
dispatcherFunc: cmdDispatchTestFuzzerHandler,
|
||||
});
|
||||
commandHistory.push(commandObject);
|
||||
|
||||
await cmdDispatchTestFuzzerHandler(args);
|
||||
}
|
||||
|
||||
async function cmdDispatchTestFuzzerHandler(args: any) {
|
||||
// Build the project
|
||||
if (!(await buildFuzzersFromWorkspace(args.projectName, '', false))) {
|
||||
println('Build projects');
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the fuzzer for 10 seconds
|
||||
println('Running fuzzer');
|
||||
await runFuzzerHandler(
|
||||
args.projectName,
|
||||
args.fuzzerName,
|
||||
extensionConfig.numberOfSecondsForTestRuns.toString(),
|
||||
''
|
||||
);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import {println} from './logger';
|
||||
|
||||
export class ExtensionConfig {
|
||||
/** Path to the repository that will be used. */
|
||||
ossFuzzPepositoryWorkPath: string = '/tmp/oss-fuzz';
|
||||
|
||||
/** The directory where crash info is stored. */
|
||||
crashesDirectory = process.env.HOME + '/oss-fuzz-crashes';
|
||||
|
||||
/** Number of seconds used for running quick test fuzzers */
|
||||
numberOfSecondsForTestRuns = 20;
|
||||
|
||||
async printConfig() {
|
||||
println('Config:');
|
||||
println('- OSS-Fuzz repository path: ' + this.ossFuzzPepositoryWorkPath);
|
||||
println('- Crashes directory: ' + this.crashesDirectory);
|
||||
println('- numberOfSecondsForTestRuns: ' + this.numberOfSecondsForTestRuns);
|
||||
}
|
||||
}
|
||||
|
||||
export const extensionConfig = new ExtensionConfig();
|
|
@ -0,0 +1,224 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import {Uri} from 'vscode';
|
||||
import {println} from './logger';
|
||||
|
||||
const path = require('path');
|
||||
let isCodeCoverageEnabled = false;
|
||||
|
||||
// create a decorator type that we use to decorate small numbers
|
||||
const codeCoveredLineDecorationType =
|
||||
vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: '#184916',
|
||||
overviewRulerColor: 'blue',
|
||||
overviewRulerLane: vscode.OverviewRulerLane.Right,
|
||||
light: {
|
||||
// this color will be used in light color themes
|
||||
borderColor: 'darkblue',
|
||||
},
|
||||
dark: {
|
||||
// this color will be used in dark color themes
|
||||
borderColor: 'lightblue',
|
||||
},
|
||||
});
|
||||
|
||||
const missingLineDecorationType = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: '#6C2B34',
|
||||
overviewRulerColor: 'blue',
|
||||
overviewRulerLane: vscode.OverviewRulerLane.Right,
|
||||
light: {
|
||||
// this color will be used in light color themes
|
||||
borderColor: 'darkblue',
|
||||
},
|
||||
dark: {
|
||||
// this color will be used in dark color themes
|
||||
borderColor: 'lightblue',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context Adds visualisation to the editor based on reading a code coverage file.
|
||||
* @param codeCoverageFile
|
||||
*/
|
||||
export async function loadCoverageIntoWorkspace(
|
||||
context: vscode.ExtensionContext,
|
||||
codeCoverageFile: Uri
|
||||
) {
|
||||
isCodeCoverageEnabled = true;
|
||||
|
||||
const doc3 = await vscode.workspace.openTextDocument(codeCoverageFile);
|
||||
const jsonCodeCoverageObj3 = JSON.parse(doc3.getText());
|
||||
|
||||
const codeCoverageMappingWithCoverage = new Map();
|
||||
const codeCoverageMapMissingCoverage = new Map();
|
||||
|
||||
Object.entries(jsonCodeCoverageObj3['files']).forEach(entry => {
|
||||
const [key, value] = entry;
|
||||
println(key);
|
||||
const filename = path.parse(key).base;
|
||||
println('Filename base: ' + filename);
|
||||
const objectDictionary: any = value as any;
|
||||
const linesWithCodeCoverage: unknown[] = [];
|
||||
println(objectDictionary['executed_lines']);
|
||||
Object.entries(objectDictionary['executed_lines']).forEach(entryInner => {
|
||||
const lineNumber = entryInner[1];
|
||||
//println("executed line: " + lineNumber);
|
||||
linesWithCodeCoverage.push(lineNumber);
|
||||
});
|
||||
codeCoverageMappingWithCoverage.set(filename, linesWithCodeCoverage);
|
||||
|
||||
const linesMissingCodeCoverage: unknown[] = [];
|
||||
Object.entries(objectDictionary['missing_lines']).forEach(entryInner => {
|
||||
const lineNumber = entryInner[1];
|
||||
//println("executed line: " + line_numb);
|
||||
linesMissingCodeCoverage.push(lineNumber);
|
||||
});
|
||||
codeCoverageMapMissingCoverage.set(filename, linesMissingCodeCoverage);
|
||||
});
|
||||
println('=========>');
|
||||
|
||||
println('Enabling code coverage decorator');
|
||||
println('decorator sample is activated');
|
||||
|
||||
let timeout: NodeJS.Timer | undefined = undefined;
|
||||
|
||||
// create a decorator type that we use to decorate large numbers
|
||||
|
||||
let activeEditor = vscode.window.activeTextEditor;
|
||||
|
||||
function updateDecorations(
|
||||
linesWithCodeCoverage: any,
|
||||
linesWithoNoCodeCoverage: any
|
||||
) {
|
||||
if (!isCodeCoverageEnabled) {
|
||||
return;
|
||||
}
|
||||
if (!activeEditor) {
|
||||
return;
|
||||
}
|
||||
println('Filename');
|
||||
println(activeEditor.document.fileName);
|
||||
|
||||
// Current file opened in the editor.
|
||||
const nameOfCurrentFile = path.parse(activeEditor.document.fileName).base;
|
||||
|
||||
println('Base filename: ' + nameOfCurrentFile);
|
||||
println('Done filename');
|
||||
const lineNumbersWithCoverage: vscode.DecorationOptions[] = [];
|
||||
const missingLineNumbers: vscode.DecorationOptions[] = [];
|
||||
|
||||
if (linesWithCodeCoverage.has(nameOfCurrentFile)) {
|
||||
println('Has this file');
|
||||
const elemWithCov = linesWithCodeCoverage.get(nameOfCurrentFile);
|
||||
for (let idx = 0; idx < elemWithCov.length; idx++) {
|
||||
const lineNo = elemWithCov[idx];
|
||||
println('Setting up: ' + lineNo);
|
||||
lineNumbersWithCoverage.push({
|
||||
range: new vscode.Range(lineNo - 1, 0, lineNo, 0),
|
||||
});
|
||||
}
|
||||
|
||||
const elemNoCov = linesWithoNoCodeCoverage.get(nameOfCurrentFile);
|
||||
for (let idx = 0; idx < elemNoCov.length; idx++) {
|
||||
const lineNo = elemNoCov[idx];
|
||||
println('Setting up: ' + lineNo);
|
||||
missingLineNumbers.push({
|
||||
range: new vscode.Range(lineNo - 1, 0, lineNo, 0),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
println('Does not have this file');
|
||||
}
|
||||
|
||||
activeEditor.setDecorations(
|
||||
codeCoveredLineDecorationType,
|
||||
lineNumbersWithCoverage
|
||||
);
|
||||
activeEditor.setDecorations(missingLineDecorationType, missingLineNumbers);
|
||||
//activeEditor.setDecorations(largeNumberDecorationType, largeNumbers);
|
||||
}
|
||||
|
||||
function triggerUpdateDecorations(
|
||||
throttle = false,
|
||||
covMap: any,
|
||||
covMisMap: any
|
||||
) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = undefined;
|
||||
}
|
||||
if (throttle) {
|
||||
//timeout = setTimeout(updateDecorations, 500);
|
||||
updateDecorations(covMap, covMisMap);
|
||||
} else {
|
||||
updateDecorations(covMap, covMisMap);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeEditor) {
|
||||
triggerUpdateDecorations(
|
||||
false,
|
||||
codeCoverageMappingWithCoverage,
|
||||
codeCoverageMapMissingCoverage
|
||||
);
|
||||
}
|
||||
|
||||
vscode.window.onDidChangeActiveTextEditor(
|
||||
editor => {
|
||||
activeEditor = editor;
|
||||
if (editor) {
|
||||
triggerUpdateDecorations(
|
||||
false,
|
||||
codeCoverageMappingWithCoverage,
|
||||
codeCoverageMapMissingCoverage
|
||||
);
|
||||
}
|
||||
},
|
||||
null,
|
||||
context.subscriptions
|
||||
);
|
||||
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
event => {
|
||||
if (activeEditor && event.document === activeEditor.document) {
|
||||
triggerUpdateDecorations(
|
||||
true,
|
||||
codeCoverageMappingWithCoverage,
|
||||
codeCoverageMapMissingCoverage
|
||||
);
|
||||
}
|
||||
},
|
||||
null,
|
||||
context.subscriptions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the values from the mappings used to track code coverage. As a
|
||||
* result, the visualisation disappears.
|
||||
*/
|
||||
export async function clearCoverage() {
|
||||
// Set global indicator.
|
||||
const activeEditor = vscode.window.activeTextEditor;
|
||||
isCodeCoverageEnabled = false;
|
||||
if (activeEditor) {
|
||||
activeEditor.setDecorations(codeCoveredLineDecorationType, []);
|
||||
activeEditor.setDecorations(missingLineDecorationType, []);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {clearCoverage} from './coverageHelper';
|
||||
import {println} from './logger';
|
||||
|
||||
// Import the command dispatcher functions
|
||||
import {cmdInputCollectorRunSpecificFuzzer} from './commands/cmdRunFuzzer';
|
||||
import {cmdInputCollectorBuildFuzzersFromWorkspace} from './commands/cmdBuildFuzzerFromWorkspace';
|
||||
import {cmdDispatcherRe} from './commands/cmdRedo';
|
||||
import {setupCIFuzzHandler} from './commands/cmdSetupCIFuzz';
|
||||
import {cmdInputCollectorTestFuzzer} from './commands/cmdTestFuzzer';
|
||||
import {displayCodeCoverageFromOssFuzz} from './commands/cmdDisplayCoverage';
|
||||
import {createOssFuzzSetup} from './commands/cmdCreateOSSFuzzSetup';
|
||||
import {runEndToEndAndGetCoverage} from './commands/cmdEndToEndCoverage';
|
||||
import {listFuzzersHandler} from './commands/cmdListFuzzers';
|
||||
import {cmdInputCollectorReproduceTestcase} from './commands/cmdReproduceTestcase';
|
||||
import {cmdDispatcherTemplate} from './commands/cmdTemplate';
|
||||
import {setUpOssFuzzHandler} from './commands/cmdSetupOSSFuzz';
|
||||
import {setOssFuzzPath} from './commands/cmdSetOSSFuzzPath';
|
||||
import {extensionConfig} from './config';
|
||||
|
||||
/**
|
||||
* Extension entrypoint. Activate the extension and register the commands.
|
||||
*/
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log('Activating extension)');
|
||||
extensionConfig.printConfig();
|
||||
println('OSS-Fuzz extension is now active!');
|
||||
|
||||
// Command registration
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.SetUp', async () => {
|
||||
println('CMD start: SetUp');
|
||||
await setUpOssFuzzHandler();
|
||||
println('CMD end: SetUp');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.RunFuzzer', async () => {
|
||||
println('CMD start: Run Fuzzer');
|
||||
//await runFuzzerHandler('', '', '', '');
|
||||
cmdInputCollectorRunSpecificFuzzer();
|
||||
println('CMD end: Run Fuzzer');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.ListFuzzers', async () => {
|
||||
println('CMD start: ListFuzzers');
|
||||
await listFuzzersHandler();
|
||||
println('CMD end: ListFuzzers');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.SetOSSFuzzPath', async () => {
|
||||
println('CMD start: SetOSSFuzzPath');
|
||||
await setOssFuzzPath();
|
||||
println('CMD end: SetOSSFuzzPath');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.GetCodeCoverage', async () => {
|
||||
println('CMD start: GetCodeCoverage');
|
||||
await displayCodeCoverageFromOssFuzz(context);
|
||||
println('CMD end: GetCodeCoverage');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.ClearCodeCoverage', async () => {
|
||||
println('CMD start: ClearCodeCoverage');
|
||||
await clearCoverage();
|
||||
println('CMD end: ClearCodeCoverage');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.CreateOSSFuzzSetup', async () => {
|
||||
println('CMD start: CreateOSSFuzzSetup');
|
||||
await createOssFuzzSetup();
|
||||
println('CMD end: CreateOSSFuzzSetup');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.WSBuildFuzzers', async () => {
|
||||
println('CMD start: WSBuildFuzzers3');
|
||||
await cmdInputCollectorBuildFuzzersFromWorkspace();
|
||||
println('CMD end: WSBuildFuzzers4');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.SetupCIFuzz', async () => {
|
||||
println('CMD start: SetupCIFuzz');
|
||||
await setupCIFuzzHandler();
|
||||
println('CMD end: SetupCIFuzz');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.testFuzzer', async () => {
|
||||
println('CMD start: testFuzzer');
|
||||
await cmdInputCollectorTestFuzzer();
|
||||
println('CMD end: testFizzer');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.testCodeCoverage', async () => {
|
||||
println('CMD start: testCodeCoverage');
|
||||
await runEndToEndAndGetCoverage(context);
|
||||
println('CMD end: testCodeCoverage');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.Reproduce', async () => {
|
||||
println('CMD start: Reproduce');
|
||||
await cmdInputCollectorReproduceTestcase();
|
||||
println('CMD end: Reproduce');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.Redo', async () => {
|
||||
println('CMD start: Re');
|
||||
await cmdDispatcherRe();
|
||||
println('CMD end: Re');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('oss-fuzz.Template', async () => {
|
||||
println('CMD start: remplate');
|
||||
await cmdDispatcherTemplate(context);
|
||||
println('CMD end: template');
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// This method is called when your extension is deactivated
|
||||
export function deactivate() {
|
||||
println('Deactivating the extension');
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
export const vscodeOutputChannel =
|
||||
vscode.window.createOutputChannel('oss-fuzz');
|
||||
|
||||
export function println(line: string) {
|
||||
vscodeOutputChannel.appendLine(line);
|
||||
}
|
||||
|
||||
export function debugPrintln(line: string) {
|
||||
console.log(line);
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const fs = require('fs');
|
||||
import * as vscode from 'vscode';
|
||||
import {
|
||||
hasOssFuzzInWorkspace,
|
||||
getOssFuzzWorkspaceProjectName,
|
||||
listFuzzersForProject,
|
||||
systemSyncLogIfFailure,
|
||||
} from './utils';
|
||||
import {println} from './logger';
|
||||
import {extensionConfig} from './config';
|
||||
|
||||
/**
|
||||
* Builds the fuzzers for a given workspace.
|
||||
*
|
||||
* There are two options:
|
||||
* 1) The fuzzers are build using the OSS-Fuzz set up in the folder
|
||||
* 2) The fuzzers are build using the workspace and then copies that over.
|
||||
*/
|
||||
export async function buildFuzzersFromWorkspace(
|
||||
projectNameArg: string,
|
||||
sanitizer: string,
|
||||
toClean: boolean
|
||||
) {
|
||||
// println('Building fuzzers locally2');
|
||||
|
||||
// Check if there is an OSS-Fuzz set up, and exit if not.
|
||||
if (
|
||||
(await isPathValidOssFuzzPath(
|
||||
extensionConfig.ossFuzzPepositoryWorkPath
|
||||
)) === false
|
||||
) {
|
||||
println('No valid oss-fuzz path');
|
||||
return false;
|
||||
}
|
||||
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolder) {
|
||||
println('No workspace folder, exiting');
|
||||
return false;
|
||||
}
|
||||
|
||||
let ossFuzzProjectName = '';
|
||||
if (await hasOssFuzzInWorkspace()) {
|
||||
/**
|
||||
* The fuzzers are in the workspace, as opposed to e.g. the oss-fuzz dirctory.
|
||||
*/
|
||||
ossFuzzProjectName = await getOssFuzzWorkspaceProjectName();
|
||||
|
||||
/**
|
||||
* The workspace has an OSS-Fuzz directory. We use this for the build.
|
||||
* This is done by copying over the relevant files to the oss-fuzz repository
|
||||
* folder. Notice that we will do a forceful copy overwriting the existing
|
||||
* project foler if it exists.
|
||||
*/
|
||||
println('Found project folder: ' + ossFuzzProjectName);
|
||||
|
||||
// Copy over the workspace oss-fuzz set up to the oss-fuzz folder.
|
||||
let cmdToExec = 'cp';
|
||||
let args: Array<string> = [
|
||||
'-rfT',
|
||||
workspaceFolder[0].uri.path + '/OSS-Fuzz/' + ossFuzzProjectName,
|
||||
extensionConfig.ossFuzzPepositoryWorkPath +
|
||||
'/projects/' +
|
||||
ossFuzzProjectName +
|
||||
'/',
|
||||
];
|
||||
|
||||
if (!(await systemSyncLogIfFailure(cmdToExec, args))) {
|
||||
println('Failed to copy project');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the fuzzers using OSS-Fuzz infrastructure.
|
||||
cmdToExec = 'python3';
|
||||
args = [
|
||||
extensionConfig.ossFuzzPepositoryWorkPath + '/infra/helper.py',
|
||||
'build_fuzzers',
|
||||
];
|
||||
println('DECIDING ABOUT SANITIZER');
|
||||
if (sanitizer !== '') {
|
||||
println('ADDING CODE COVERAGE SANITIZER');
|
||||
args.push('--sanitizer=' + sanitizer);
|
||||
}
|
||||
|
||||
if (toClean) {
|
||||
args.push('--clean');
|
||||
}
|
||||
|
||||
args.push(ossFuzzProjectName);
|
||||
println('Building fuzzers');
|
||||
if (!(await systemSyncLogIfFailure(cmdToExec, args))) {
|
||||
println('Failed to build fuzzers');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ossFuzzProjectName = projectNameArg;
|
||||
|
||||
const targetOssFuzzProject = vscode.Uri.file(
|
||||
extensionConfig.ossFuzzPepositoryWorkPath +
|
||||
'/projects/' +
|
||||
ossFuzzProjectName
|
||||
);
|
||||
// Check if the folder exists.
|
||||
let projectHasOssFuzzFolder = false;
|
||||
try {
|
||||
await vscode.workspace.fs.readDirectory(targetOssFuzzProject);
|
||||
projectHasOssFuzzFolder = true;
|
||||
} catch {
|
||||
projectHasOssFuzzFolder = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The workspace does not have a OSS-Fuzz specific folder but has
|
||||
* a folder in the OSS-Fuzz/projects/* directory. As such, we build
|
||||
* the project using that build.sh set up, but, instead of cloning
|
||||
* the repository we mount the workspace root onto what would normally
|
||||
* be cloned.
|
||||
*/
|
||||
if (projectHasOssFuzzFolder) {
|
||||
// println('Found a target directory');
|
||||
|
||||
// Build the fuzzers using OSS-Fuzz infrastructure.
|
||||
// First, Set up a temporary workpath that will be cleanup after
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
const cmdToExec2 = 'cp';
|
||||
const temporaryProjectPath =
|
||||
extensionConfig.ossFuzzPepositoryWorkPath +
|
||||
'/projects/' +
|
||||
ossFuzzProjectName +
|
||||
'/temporary-project';
|
||||
|
||||
const args2: Array<string> = [
|
||||
'-rfT',
|
||||
wsPath.toString(),
|
||||
temporaryProjectPath,
|
||||
];
|
||||
|
||||
if (!(await systemSyncLogIfFailure(cmdToExec2, args2))) {
|
||||
println('Failed to build fuzzers');
|
||||
return false;
|
||||
}
|
||||
|
||||
//const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
const temporaryDockerPath =
|
||||
extensionConfig.ossFuzzPepositoryWorkPath +
|
||||
'/projects/' +
|
||||
ossFuzzProjectName +
|
||||
'/Dockerfile';
|
||||
const temporaryDockerPath2 =
|
||||
extensionConfig.ossFuzzPepositoryWorkPath +
|
||||
'/projects/' +
|
||||
ossFuzzProjectName +
|
||||
'/Dockerfile2';
|
||||
|
||||
const args3: Array<string> = [temporaryDockerPath, temporaryDockerPath2];
|
||||
if (!(await systemSyncLogIfFailure('cp', args3))) {
|
||||
println('Failed to copy Dockerfile');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Append COPY command to Dockerfile
|
||||
fs.appendFileSync(
|
||||
temporaryDockerPath,
|
||||
'COPY temporary-project /src/' + ossFuzzProjectName
|
||||
);
|
||||
|
||||
// Second, build the actual fuzzers using the temporarily created project path for mount.
|
||||
const cmdToExec = 'python3';
|
||||
const args = [
|
||||
extensionConfig.ossFuzzPepositoryWorkPath + '/infra/helper.py',
|
||||
'build_fuzzers', // command
|
||||
];
|
||||
|
||||
// Add sanitizer if needed.
|
||||
if (sanitizer !== '') {
|
||||
args.push('--sanitizer=' + sanitizer);
|
||||
}
|
||||
|
||||
// Add clean flag if needed.
|
||||
if (toClean) {
|
||||
args.push('--clean');
|
||||
}
|
||||
|
||||
args.push(ossFuzzProjectName);
|
||||
/*
|
||||
Previously we used OSS-Fuzz logic that supports mounting paths for getting
|
||||
the workspace into the Dockerfile.
|
||||
This approach, however, has limitations in that most builds will modify
|
||||
the contents of the folder they're working in. This can cause issues and also
|
||||
make it not possible to build several versions of the project with changing
|
||||
sanitizers in a sequence. As such, we disbanded.
|
||||
*/
|
||||
println('Building fuzzers');
|
||||
if (!(await systemSyncLogIfFailure(cmdToExec, args))) {
|
||||
println('Failed to copy Dockerfile');
|
||||
// Move back the modified Dockerfile
|
||||
const args5: Array<string> = [
|
||||
temporaryDockerPath2,
|
||||
temporaryDockerPath,
|
||||
];
|
||||
if (!(await systemSyncLogIfFailure('mv', args5))) {
|
||||
println('Failed to copy back Dockerfile');
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move back the modified Dockerfile
|
||||
const args5: Array<string> = [temporaryDockerPath2, temporaryDockerPath];
|
||||
if (!(await systemSyncLogIfFailure('mv', args5))) {
|
||||
println('Failed to copy back Dockerfile');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
println('OSS-Fuzz does not have the relevant project folder');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we go to here we successfully build the project. Give information.
|
||||
vscode.window.showInformationMessage('Successfully build project');
|
||||
|
||||
// List the fuzzers build
|
||||
await listFuzzersForProject(
|
||||
ossFuzzProjectName,
|
||||
extensionConfig.ossFuzzPepositoryWorkPath
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the fuzzer for a given project.
|
||||
*/
|
||||
export async function runFuzzerHandler(
|
||||
projectNameArg: string,
|
||||
fuzzerNameArg: string,
|
||||
secondsToRunArg: string,
|
||||
fuzzerCorpusPath: string
|
||||
) {
|
||||
// Check there is a valid OSS-Fuzz path. If not, bail out.
|
||||
if (
|
||||
(await isPathValidOssFuzzPath(
|
||||
extensionConfig.ossFuzzPepositoryWorkPath
|
||||
)) === false
|
||||
) {
|
||||
println('Missing valid OSS-Fuzz path.');
|
||||
return;
|
||||
}
|
||||
// The fuzzer is run by way of OSS-Fuzz's helper.py so we use python3 to launch
|
||||
// this script.
|
||||
const cmdToExec = 'python3';
|
||||
|
||||
// Set the arguments correctly. The ordering here is important for compatibility
|
||||
// with the underlying argparse used by OSS-Fuzz helper.py.
|
||||
const args: Array<string> = [
|
||||
extensionConfig.ossFuzzPepositoryWorkPath + '/infra/helper.py',
|
||||
'run_fuzzer',
|
||||
];
|
||||
if (fuzzerCorpusPath !== '') {
|
||||
args.push('--corpus-dir');
|
||||
args.push(fuzzerCorpusPath);
|
||||
}
|
||||
args.push(projectNameArg);
|
||||
args.push(fuzzerNameArg);
|
||||
args.push('--');
|
||||
args.push('-max_total_time=' + secondsToRunArg);
|
||||
|
||||
println(
|
||||
'Running fuzzer' +
|
||||
fuzzerNameArg +
|
||||
' from project ' +
|
||||
projectNameArg +
|
||||
' for ' +
|
||||
secondsToRunArg +
|
||||
' seconds.'
|
||||
);
|
||||
|
||||
// Run the actual command
|
||||
if (!(await systemSyncLogIfFailure(cmdToExec, args))) {
|
||||
println('Failed to run fuzzer');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validates if a directory is a valid oss-fuzz path.
|
||||
export async function isPathValidOssFuzzPath(path: string) {
|
||||
try {
|
||||
if (await vscode.workspace.fs.readDirectory(vscode.Uri.file(path))) {
|
||||
// println('Is a directory');
|
||||
// const helperPath = vscode.Uri.file(path + '/infra/helper.py');
|
||||
const helperPath = path + '/infra/helper.py';
|
||||
//console.log('Checking ' + helperPath.toString());
|
||||
if (fs.existsSync(helperPath.toString())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import path = require('path');
|
||||
import {println} from './logger';
|
||||
|
||||
export async function setupProjectInitialFiles() {
|
||||
const wsedit = new vscode.WorkspaceEdit();
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
|
||||
const projectGithubRepository = await vscode.window.showInputBox({
|
||||
value: '',
|
||||
placeHolder: 'Github repository for the project.',
|
||||
});
|
||||
if (!projectGithubRepository) {
|
||||
return;
|
||||
}
|
||||
|
||||
const projectNameFromRepo = path.parse(projectGithubRepository).base;
|
||||
println('Derived project name: ' + projectNameFromRepo);
|
||||
|
||||
const pythonFiles = await vscode.workspace.findFiles('**/*.py');
|
||||
const cppFiles = await vscode.workspace.findFiles('**/*.c++');
|
||||
const cfiles = await vscode.workspace.findFiles('**/*.c');
|
||||
const rustFiles = await vscode.workspace.findFiles('**/*.rust');
|
||||
const golangFiles = await vscode.workspace.findFiles('**/*.go');
|
||||
|
||||
println('Number of python files: ' + pythonFiles.length);
|
||||
println('Number of C++ files: ' + cppFiles.length);
|
||||
println('Number of C files: ' + cfiles.length);
|
||||
println('Number of rustFiles files: ' + rustFiles.length);
|
||||
println('Number of golangFiles files: ' + golangFiles.length);
|
||||
|
||||
const maxCount = Math.max(
|
||||
pythonFiles.length,
|
||||
cppFiles.length,
|
||||
cfiles.length,
|
||||
rustFiles.length,
|
||||
golangFiles.length
|
||||
);
|
||||
let target = '';
|
||||
if (maxCount === pythonFiles.length) {
|
||||
target = 'python';
|
||||
} else {
|
||||
target = 'not implemented';
|
||||
}
|
||||
|
||||
println('Target language: ' + target);
|
||||
|
||||
if (workspaceFolder) {
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
|
||||
const ossfuzzDockerFilepath = vscode.Uri.file(
|
||||
wsPath + '/OSS-Fuzz/' + projectNameFromRepo + '/Dockerfile'
|
||||
);
|
||||
|
||||
vscode.window.showInformationMessage(ossfuzzDockerFilepath.toString());
|
||||
wsedit.createFile(ossfuzzDockerFilepath, {ignoreIfExists: true});
|
||||
|
||||
const todaysDate = new Date();
|
||||
const currentYear = todaysDate.getFullYear();
|
||||
|
||||
if (target === 'python') {
|
||||
const dockerfileTemplate = `# Copyright ${currentYear} Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
FROM gcr.io/oss-fuzz-base/base-builder-python
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
RUN git clone --depth 1 ${projectGithubRepository} ${projectNameFromRepo}
|
||||
WORKDIR ${projectNameFromRepo}
|
||||
COPY build.sh *.py $SRC/`;
|
||||
wsedit.insert(
|
||||
ossfuzzDockerFilepath,
|
||||
new vscode.Position(0, 0),
|
||||
dockerfileTemplate
|
||||
);
|
||||
|
||||
const ossfuzzBuildFilepath = vscode.Uri.file(
|
||||
wsPath + '/OSS-Fuzz/' + projectNameFromRepo + '/build.sh'
|
||||
);
|
||||
vscode.window.showInformationMessage(ossfuzzBuildFilepath.toString());
|
||||
wsedit.createFile(ossfuzzBuildFilepath, {ignoreIfExists: true});
|
||||
const buildTemplate = `#!/bin/bash -eu
|
||||
# Copyright ${currentYear} Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
python3 -m pip install .
|
||||
|
||||
# Build fuzzers (files prefixed with fuzz_) to $OUT
|
||||
for fuzzer in $(find $SRC -name 'fuzz_*.py'); do
|
||||
compile_python_fuzzer $fuzzer
|
||||
done`;
|
||||
wsedit.insert(
|
||||
ossfuzzBuildFilepath,
|
||||
new vscode.Position(0, 0),
|
||||
buildTemplate
|
||||
);
|
||||
|
||||
// project.yaml
|
||||
const projectYamlFilepath = vscode.Uri.file(
|
||||
wsPath + '/OSS-Fuzz/' + projectNameFromRepo + '/project.yaml'
|
||||
);
|
||||
vscode.window.showInformationMessage(projectYamlFilepath.toString());
|
||||
wsedit.createFile(projectYamlFilepath, {ignoreIfExists: true});
|
||||
const projectYamlTemplate = `homepage: "${projectGithubRepository}"
|
||||
language: python
|
||||
primary_contact: "<primary_contact_email>"
|
||||
main_repo: "${projectGithubRepository}"
|
||||
file_github_issue: true
|
||||
`;
|
||||
wsedit.insert(
|
||||
projectYamlFilepath,
|
||||
new vscode.Position(0, 0),
|
||||
projectYamlTemplate
|
||||
);
|
||||
|
||||
/* Sample template fuzzer */
|
||||
const sampleFuzzFile = vscode.Uri.file(
|
||||
wsPath + '/OSS-Fuzz/' + projectNameFromRepo + '/fuzz_ex1.py'
|
||||
);
|
||||
vscode.window.showInformationMessage(projectYamlFilepath.toString());
|
||||
wsedit.createFile(sampleFuzzFile, {ignoreIfExists: true});
|
||||
const sampleFuzzFileContents = `import sys
|
||||
import atheris
|
||||
|
||||
with atheris.instrument_imports():
|
||||
# Import your target modules here to have them
|
||||
# instrumented by the fuzzer, e.g:
|
||||
# import MODULE_NAME
|
||||
pass
|
||||
|
||||
@atheris.instrument_func
|
||||
def TestOneInput(data):
|
||||
fdp = atheris.FuzzedDataProvider(data)
|
||||
|
||||
|
||||
def main():
|
||||
# atheris.instrument_all()
|
||||
atheris.Setup(sys.argv, TestOneInput)
|
||||
atheris.Fuzz()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()`;
|
||||
|
||||
wsedit.insert(
|
||||
sampleFuzzFile,
|
||||
new vscode.Position(0, 0),
|
||||
sampleFuzzFileContents
|
||||
);
|
||||
|
||||
const readmeFile = vscode.Uri.file(wsPath + '/OSS-Fuzz/' + '/README.md');
|
||||
vscode.window.showInformationMessage(readmeFile.toString());
|
||||
wsedit.createFile(readmeFile, {ignoreIfExists: true});
|
||||
const readmeContents = `# OSS-Fuzz set up
|
||||
This folder is the OSS-Fuzz set up.
|
||||
`;
|
||||
|
||||
wsedit.insert(readmeFile, new vscode.Position(0, 0), readmeContents);
|
||||
vscode.workspace.applyEdit(wsedit);
|
||||
}
|
||||
|
||||
vscode.window.showInformationMessage('Created a new file: hello/world.md');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
const fs = require('fs');
|
||||
const {spawn} = require('node:child_process');
|
||||
|
||||
import {println, debugPrintln} from './logger';
|
||||
|
||||
/**
|
||||
* Checks if the current workspace has a generated OSS-Fuzz folder. This is the
|
||||
* generated folder from our auto-generation capabilities.
|
||||
*
|
||||
* @returns boolean
|
||||
*/
|
||||
export async function hasOssFuzzInWorkspace() {
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
|
||||
if (!workspaceFolder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Identify if the workspace folder has a OSS-Fuzz set up.
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
const ossfuzzDockerFilepath = vscode.Uri.file(wsPath + '/OSS-Fuzz/');
|
||||
try {
|
||||
if (await vscode.workspace.fs.readDirectory(ossfuzzDockerFilepath)) {
|
||||
for (const [name, type] of await vscode.workspace.fs.readDirectory(
|
||||
ossfuzzDockerFilepath
|
||||
)) {
|
||||
// If it's a directory then we know we have the project set up in there.
|
||||
if (type === 2) {
|
||||
// We assume this is the project folder for now.
|
||||
println('Found the relevant directory: ' + name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the project name of the integrated OSS-Fuzz project.
|
||||
* @returns string
|
||||
*/
|
||||
export async function getOssFuzzWorkspaceProjectName() {
|
||||
const workspaceFolder = vscode.workspace.workspaceFolders;
|
||||
if (!workspaceFolder) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
// Identify if the workspace folder has a OSS-Fuzz set up.
|
||||
const wsPath = workspaceFolder[0].uri.fsPath; // gets the path of the first workspace folder
|
||||
const ossfuzzDockerFilepath = vscode.Uri.file(wsPath + '/OSS-Fuzz/');
|
||||
try {
|
||||
if (await vscode.workspace.fs.readDirectory(ossfuzzDockerFilepath)) {
|
||||
for (const [name, type] of await vscode.workspace.fs.readDirectory(
|
||||
ossfuzzDockerFilepath
|
||||
)) {
|
||||
if (type === 2) {
|
||||
// println('Is a directory');
|
||||
// We assume this is the project folder for now.
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the fuzzers available in the OSS-Fuzz build project.
|
||||
*
|
||||
* @param projectName
|
||||
* @param ossFuzzRepositoryPath
|
||||
* @returns
|
||||
*/
|
||||
export async function listFuzzersForProject(
|
||||
projectName: string,
|
||||
ossFuzzRepositoryPath: string
|
||||
) {
|
||||
const projectOssFuzzBuildPath = vscode.Uri.file(
|
||||
ossFuzzRepositoryPath + '/build/out/' + projectName
|
||||
);
|
||||
const fuzzersInProject: Array<string> = [];
|
||||
for (const [name, type] of await vscode.workspace.fs.readDirectory(
|
||||
projectOssFuzzBuildPath
|
||||
)) {
|
||||
// Is it a file?
|
||||
if (type === 1) {
|
||||
const filepath =
|
||||
ossFuzzRepositoryPath + '/build/out/' + projectName + '/' + name;
|
||||
const binary = fs.readFileSync(filepath);
|
||||
|
||||
// Check if fuzzer entrypoint exists in file. This is similar to how OSS-Fuzz
|
||||
// checks whether a file is fuzzer or not.
|
||||
if (binary.lastIndexOf('LLVMFuzzerTestOneInput') !== -1) {
|
||||
fuzzersInProject.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
println('Successfully build the project.');
|
||||
println('The fuzzers in project');
|
||||
for (const fuzzName of fuzzersInProject) {
|
||||
println(fuzzName);
|
||||
}
|
||||
return fuzzersInProject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper functions for identifying the primary programming language of the workspace.
|
||||
*
|
||||
* This is achieved by identifying the suffix of files and then sorting
|
||||
* based on those with most of a given language supported by OSS-Fuzz.
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export async function determineWorkspaceLanguage() {
|
||||
const pythonFiles = await vscode.workspace.findFiles('**/*.py');
|
||||
const cppFiles = await vscode.workspace.findFiles('**/*.c++');
|
||||
const cfiles = await vscode.workspace.findFiles('**/*.c');
|
||||
const rustFiles = await vscode.workspace.findFiles('**/*.rust');
|
||||
const golangFiles = await vscode.workspace.findFiles('**/*.go');
|
||||
|
||||
println('Number of python files: ' + pythonFiles.length);
|
||||
println('Number of C++ files: ' + cppFiles.length);
|
||||
println('Number of C files: ' + cfiles.length);
|
||||
println('Number of rustFiles files: ' + rustFiles.length);
|
||||
println('Number of golangFiles files: ' + golangFiles.length);
|
||||
|
||||
const maxCount = Math.max(
|
||||
pythonFiles.length,
|
||||
cppFiles.length,
|
||||
cfiles.length,
|
||||
rustFiles.length,
|
||||
golangFiles.length
|
||||
);
|
||||
let target = '';
|
||||
if (maxCount === pythonFiles.length) {
|
||||
target = 'python';
|
||||
} else if (maxCount === cppFiles.length) {
|
||||
target = 'c++';
|
||||
} else if (maxCount === cfiles.length) {
|
||||
target = 'c';
|
||||
} else if (maxCount === rustFiles.length) {
|
||||
target = 'rust';
|
||||
} else if (maxCount === golangFiles.length) {
|
||||
target = 'golang';
|
||||
} else {
|
||||
target = 'not implemented';
|
||||
}
|
||||
|
||||
println('Target language: ' + target);
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to execute commands on the system.
|
||||
*/
|
||||
export async function systemSync(cmd: string, args: Array<string | undefined>) {
|
||||
debugPrintln('Running command');
|
||||
debugPrintln(cmd);
|
||||
debugPrintln(args.toString());
|
||||
debugPrintln('<<<<<<<<<<<<');
|
||||
|
||||
// Launch the command
|
||||
const command = spawn(cmd, args);
|
||||
|
||||
// Callbacks for output events, to capture stdout and stderr live.
|
||||
command.stdout.on('data', (x: {toString: () => string}) => {
|
||||
println(x.toString());
|
||||
});
|
||||
command.stderr.on('data', (x: {toString: () => string}) => {
|
||||
println(x.toString());
|
||||
});
|
||||
|
||||
// Monitor for child exit.
|
||||
let hasChildExited = 0;
|
||||
let childExitCode = 0;
|
||||
command.on('exit', (code: any, signal: any) => {
|
||||
// println('child process exited with ' + `code ${code} and signal ${signal}`);
|
||||
childExitCode = code;
|
||||
hasChildExited = 1;
|
||||
});
|
||||
|
||||
// Block until the child process has exited.
|
||||
const snooze = (ms: number) =>
|
||||
new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
let idx = 0;
|
||||
const maxSeconds = 1800;
|
||||
debugPrintln('Child exited: ' + hasChildExited);
|
||||
|
||||
// I think we can convert the following loop to a Promise wrapping the command
|
||||
// exeuction. TODO(David).
|
||||
while (hasChildExited === 0 && idx < maxSeconds) {
|
||||
idx += 1;
|
||||
await snooze(1000);
|
||||
}
|
||||
|
||||
// Command execution is done, return appropriately if success/error.
|
||||
if (childExitCode !== 0) {
|
||||
println('Command execution errored');
|
||||
return [false, command.toString()];
|
||||
}
|
||||
// println('Succes');
|
||||
return [true, command.toString()];
|
||||
}
|
||||
|
||||
export async function systemSyncLogIfFailure(
|
||||
cmd: string,
|
||||
args: Array<string | undefined>
|
||||
): Promise<boolean> {
|
||||
const [res, cmdMsg] = await systemSync(cmd, args);
|
||||
if (res === false) {
|
||||
println(cmdMsg);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"extends": "./node_modules/gts/tsconfig-google.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "build"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue