diff --git a/projects/typescript/build.sh b/projects/typescript/build.sh index b42e9f71a..33d50ba6a 100644 --- a/projects/typescript/build.sh +++ b/projects/typescript/build.sh @@ -20,7 +20,6 @@ npm install npm install --save-dev @jazzer.js/core # Build Fuzzers. -# Fuzzing is kinda slow and a resource hog so we have to adjust the timeout and -# memory limit to satisfy libfuzzer -compile_javascript_fuzzer TypeScript fuzz_ast -i typescript --sync --timeout=30000 -- -rss_limit_mb=4096 -compile_javascript_fuzzer TypeScript fuzz_compiler -i typescript --sync --timeout=30000 -- -rss_limit_mb=4096 +# Fuzzing TS is a resource hog so we have to adjust the rss limit a bit +compile_javascript_fuzzer TypeScript fuzz_ast -i typescript -- -rss_limit_mb=4096 +compile_javascript_fuzzer TypeScript fuzz_compiler -i typescript --sync -- -rss_limit_mb=4096 diff --git a/projects/typescript/fuzz_ast.js b/projects/typescript/fuzz_ast.js index 907c6f189..47749b916 100644 --- a/projects/typescript/fuzz_ast.js +++ b/projects/typescript/fuzz_ast.js @@ -14,17 +14,16 @@ // //////////////////////////////////////////////////////////////////////////////// -const { FuzzedDataProvider } = require('@jazzer.js/core'); -const ts = require('typescript'); +const { FuzzedDataProvider } = require("@jazzer.js/core"); +const ts = require("typescript"); -module.exports.fuzz = function(data) { +module.exports.fuzz = async function(data) { const provider = new FuzzedDataProvider(data); try { - const fileName = provider.consumeString(10) + '.ts'; + const fileName = provider.consumeString(10) + ".ts"; const fileContents = provider.consumeString(1000); - // Parse the source file const sourceFile = ts.createSourceFile( fileName, fileContents, @@ -32,9 +31,12 @@ module.exports.fuzz = function(data) { /*setParentNodes */ true ); - // Get the diagnostics for the source file + // Fuzzing parsing and lexing ts.getPreEmitDiagnostics(sourceFile); + // Fuzzing type inference + ts.getTypeChecker(sourceFile); + // Consume a boolean and use it to randomly remove a node from the AST const shouldRemoveNode = provider.consumeBoolean(); if (shouldRemoveNode) { @@ -75,13 +77,124 @@ module.exports.fuzz = function(data) { ts.replaceNode(nodeToReplace, newNode); } } + + // Fuzzing transformation and emit + const transformed = ts.transform(sourceFile, [/* transformation functions */]); + const transformedSourceFile = transformed.transformed[0]; + + // Fuzzing language features + const shouldFuzzLanguageFeature = provider.consumeBoolean(); + if (shouldFuzzLanguageFeature) { + // Fuzzing classes + const classDeclaration = ts.createClassDeclaration( + /* decorators */[], + /* modifiers */[], + provider.consumeString(10), + /* typeParameters */[], + /* heritageClauses */[], + /* members */[] + ); + ts.addDeclaration(sourceFile, classDeclaration); + + // Fuzzing interfaces + const interfaceDeclaration = ts.createInterfaceDeclaration( + /* decorators */[], + /* modifiers */[], + provider.consumeString(10), + /* typeParameters */[], + /* heritageClauses */[], + /* members */[] + ); + ts.addDeclaration(sourceFile, interfaceDeclaration); + + // Fuzzing modules + const moduleDeclaration = ts.createModuleDeclaration( + /* decorators */[], + /* modifiers */[], + ts.createIdentifier(provider.consumeString(10)), + ts.createModuleBlock([]), + ts.NodeFlags.Namespace + ); + ts.addDeclaration(sourceFile, moduleDeclaration); + + // Fuzzing generics + const genericFunctionDeclaration = ts.createFunctionDeclaration( + /* decorators */[], + /* modifiers */[], + /* asteriskToken */ undefined, + provider.consumeString(10), + /* typeParameters */[ + ts.createTypeParameterDeclaration( + ts.createIdentifier(provider.consumeString(10)), + /* constraint */ undefined, + /* defaultType */ undefined + ) + ], + /* parameters */[], + /* type */ undefined, + /* body */ undefined + ); + ts.addDeclaration(sourceFile, genericFunctionDeclaration); + + // Fuzzing decorators + const decorator = ts.createDecorator( + ts.createCall( + ts.createIdentifier(provider.consumeString(10)), + /* typeArguments */[], + /* argumentsArray */[] + ) + ); + ts.addDeclaration(sourceFile, decorator); + + // Fuzzing async/await + const asyncFunctionDeclaration = ts.createFunctionDeclaration( + /* decorators */[], + /* modifiers */[], + /* asteriskToken */ undefined, + provider.consumeString(10), + /* typeParameters */[], + /* parameters */[], + /* type */ undefined, + ts.createBlock([ + ts.createAwaitExpression( + ts.createCall( + ts.createIdentifier(provider.consumeString(10)), + /* typeArguments */[], + /* argumentsArray */[] + ) + ) + ]) + ); + ts.addDeclaration(sourceFile, asyncFunctionDeclaration); + } + + // Fuzzing compiler options + const compilerOptions = { + target: ts.ScriptTarget.ES5, + module: ts.ModuleKind.CommonJS, + strict: provider.consumeBoolean(), + // ... + }; + const program = ts.createProgram([fileName], compilerOptions); + program.emit(); + + // Fuzzing API functions + const shouldFuzzApiFunction = provider.consumeBoolean(); + if (shouldFuzzApiFunction) { + // Fuzzing type checking + const typeChecker = program.getTypeChecker(); + const randomSymbol = typeChecker.getSymbolAtLocation(sourceFile); + typeChecker.getTypeOfSymbolAtLocation(randomSymbol, sourceFile); + // ... + } + } catch (error) { if (!ignoredError(error)) throw error; } - }; + function ignoredError(error) { - return !!ignored.find((message) => error.message.indexOf(message) !== -1); + return !!ignored.find(message => error.message.indexOf(message) !== -1); } const ignored = [ diff --git a/projects/typescript/fuzz_compiler.js b/projects/typescript/fuzz_compiler.js index 4649f5bd1..a4a107bb9 100644 --- a/projects/typescript/fuzz_compiler.js +++ b/projects/typescript/fuzz_compiler.js @@ -14,28 +14,21 @@ // //////////////////////////////////////////////////////////////////////////////// -const { FuzzedDataProvider } = require('@jazzer.js/core'); -const ts = require('typescript'); +const { FuzzedDataProvider } = require("@jazzer.js/core"); +const ts = require("typescript"); module.exports.fuzz = function(data) { const provider = new FuzzedDataProvider(data); try { - // Generate a random file name and path. - const fileName = provider.consumeString(10) + '.ts'; - const filePath = provider.consumeString(10) + '/' + fileName; + const fileName = provider.consumeString(10) + ".ts"; + const filePath = provider.consumeString(10) + "/" + fileName; + const fileContent = provider.consumeString(100); - // Generate a random file content. - const fileContent = provider.consumeString(1000); - - // Create a source file from the generated file content. const sourceFile = ts.createSourceFile(fileName, fileContent, ts.ScriptTarget.Latest); - // Parse the source file. const result = ts.parseSourceFile(sourceFile, ts.ScriptTarget.Latest, true); - - // Consume the diagnostics array generated by the parseSourceFile function. - const diagnostics = result.diagnostics.map(diagnostic => { + const _diagnostics = result.diagnostics.map(diagnostic => { return { message: diagnostic.messageText, start: diagnostic.start, @@ -44,7 +37,7 @@ module.exports.fuzz = function(data) { }; }); - // Generate random inputs for additional TypeScript compiler API functions. + const program = ts.createProgram([fileName], { allowJs: true }); const printer = ts.createPrinter(); const nodes = ts.createNodeArray([sourceFile]); @@ -59,13 +52,20 @@ module.exports.fuzz = function(data) { ts.createSymbol(ts.SymbolFlags.Type, identifier); ts.createType(typeNode); - ts.createWatchCompilerHost([fileName], { allowJs: true }, ts.sys, ts.createSemanticDiagnosticsBuilderProgram, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); - } catch (error) { + const watchCompilerHost = ts.createWatchCompilerHost([fileName], { allowJs: true }, ts.sys, ts.createSemanticDiagnosticsBuilderProgram, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + + program.getTypeChecker(); + program.emit(); + program.getTypeRoots(); + ts.getParsedCommandLineOfConfigFile(fileName, {}, ts.sys).errors; + program.getDeclarationDiagnostics(); + } + catch (error) { if (!ignoredError(error)) { throw error; } } -} +}; function ignoredError(error) { return !!ignored.find((message) => error.message.indexOf(message) !== -1);