├── test ├── bins │ ├── ls │ └── files │ │ ├── sarif.rules.test.json │ │ ├── sarif.rules.ghidra.json │ │ ├── sarif.driver.ns.json │ │ ├── sarif.rules.mastg.json │ │ └── sarif.results.r2sarif.json └── db │ └── cmd │ ├── sarif_flows │ ├── cmd_sarif │ └── sarif_add ├── dist └── debian │ ├── DESCR │ ├── CONFIG │ ├── Makefile │ └── deb.mk ├── src ├── README.md ├── examples │ ├── verify.ts │ ├── Makefile │ ├── hello.ts │ └── test-sarif.json ├── Makefile ├── tsconfig.json ├── sarif │ ├── template.ts │ ├── utilsgen.ts │ ├── utilsverify.ts │ ├── parser.ts │ ├── generator.ts │ ├── types.ts │ └── index.ts ├── package.json ├── plugin.r2.ts └── package-lock.json ├── doc └── PLAN.md ├── Makefile ├── .github └── workflows │ └── ci.yml ├── README.md └── Attic └── sarif.r2.js /test/bins/ls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radareorg/r2sarif/main/test/bins/ls -------------------------------------------------------------------------------- /dist/debian/DESCR: -------------------------------------------------------------------------------- 1 | SARIF support for radare2 2 | radare2 plugin to import/export Static Analysis Results Interchange Format (SARIF) documents to let the analyst report and visualize vulnerabilities in a binary. 3 | -------------------------------------------------------------------------------- /dist/debian/CONFIG: -------------------------------------------------------------------------------- 1 | PACKAGE=r2sarif 2 | DEPENDS=radare2 3 | SECTION=user/shell 4 | PRIORITY=optional 5 | MAINTAINER=pancake 6 | VERSION=$(shell git tag | tail -n 1) 7 | # arch 8 | UNAMEM=$(shell uname -m) 9 | ifeq ($(UNAMEM),x86_64) 10 | ARCH=amd64 11 | else 12 | ARCH=arm64 13 | endif 14 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # sarif typescript api 2 | 3 | High level API to parse and generate sarif documents in TypeScript. 4 | 5 | ## Documentation 6 | 7 | * https://github.com/oasis-tcs/sarif-spec/tree/main/sarif-2.2/schema 8 | * https://docs.oasis-open.org/sarif/sarif/v2.0/sarif-v2.0.pdf 9 | 10 | ## Author 11 | 12 | --pancake <@nowsecure.com> 13 | -------------------------------------------------------------------------------- /src/examples/verify.ts: -------------------------------------------------------------------------------- 1 | import { verifySarif, readFileSync } from "../sarif/utilsverify.js"; 2 | 3 | const data: any = JSON.parse(readFileSync("test-sarif.json", "utf-8")); 4 | const result = verifySarif(data); 5 | if (result === true) { 6 | console.log("SARIF document is valid"); 7 | } else { 8 | console.error(result); 9 | for (let err of result) { 10 | console.log(err.instancePath + " " + err.message); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | tsc 3 | 4 | vs vsc: 5 | open -a "Visual Studio Code" . 6 | 7 | test: 8 | $(MAKE) -C .. test 9 | 10 | xxtest: 11 | make -C examples/hello 12 | 13 | lint: 14 | ./node_modules/.bin/eslint --fix sarif/*.ts 15 | 16 | r2: plugin.r2.js 17 | r2 -i plugin.r2.js -c sarif /bin/ls 18 | 19 | plugin.r2.js: plugin.r2.ts 20 | rm -f sarif/*.js 21 | cd sarif && r2pm -r r2frida-compile -o types.js types.ts 22 | r2pm -r r2frida-compile -o plugin.r2.js plugin.r2.ts 23 | 24 | pub: 25 | npm pub 26 | 27 | .PHONY: pub r2 lint test vs vsc all 28 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "sarif", 4 | "moduleResolution": "nodenext", 5 | "target": "esnext", 6 | "esModuleInterop": true, 7 | "resolveJsonModule": true, 8 | "module": "nodenext", 9 | "lib": ["esnext"], 10 | "declaration": true, 11 | "strict": false, 12 | "skipLibCheck": true, 13 | "outDir": "./dist", 14 | "rootDir": "./sarif" 15 | }, 16 | "paths": { 17 | "*": ["node_modules/*", "src/types/*"] 18 | }, 19 | "include": [ 20 | "sarif/index.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/db/cmd/sarif_flows: -------------------------------------------------------------------------------- 1 | NAME=sarif codeflows 2 | FILE=- 3 | ARGS=-i ../sarif.r2.js 4 | CMDS=< sarif-schema-2.1.0.json 9 | rm -f tmp 10 | 11 | sync22: 12 | # not standard? 13 | wget -O tmp https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.2/schema/sarif-2-2.schema.json 14 | mv tmp sarif-schema-2.2.0.json 15 | 16 | sync: sync21 sync22 17 | -------------------------------------------------------------------------------- /dist/debian/Makefile: -------------------------------------------------------------------------------- 1 | include ./CONFIG 2 | 3 | UNAME=$(shell uname) 4 | SUDO?=sudo 5 | DEPENDS= 6 | CROSSARCH=x64 7 | R2CFG_FLAGS?= 8 | PWD=$(shell pwd) 9 | PACKAGE_DIR?=${PWD} 10 | 11 | R2_VERSION=$(shell r2 -qv) 12 | 13 | DOCKCROSS=$(PWD)/../dockcross 14 | R2PLUGDIR=/usr/lib/radare2/$(R2_VERSION) 15 | 16 | all: root 17 | $(SUDO) rm -rf control data 18 | $(MAKE) clean 19 | mkdir -p data 20 | cp -rf root/* data 21 | $(MAKE) control 22 | $(MAKE) deb 23 | 24 | root: 25 | mkdir -p root/$(R2PLUGDIR) 26 | $(MAKE) -C ../.. 27 | cp -f ../../sarif.r2.js root/$(R2PLUGDIR) 28 | 29 | purge: clean 30 | rm -rf root 31 | 32 | summary: 33 | echo $(VERSION) 34 | 35 | include deb.mk 36 | -------------------------------------------------------------------------------- /src/sarif/template.ts: -------------------------------------------------------------------------------- 1 | export const sarifSchemeVersion = "2.1.0"; 2 | 3 | // TODO: move into utils 4 | export const sarifRadareTemplate = { 5 | $schema: 'http://json.schemastore.org/sarif-' + sarifSchemeVersion, 6 | version: sarifSchemeVersion, 7 | runs: [ 8 | { 9 | tool: { 10 | driver: { 11 | name: 'radare2', 12 | semanticVersion: '1.0.0', 13 | rules: [ 14 | ] 15 | } 16 | }, 17 | results: [ 18 | ] 19 | } 20 | ] 21 | }; 22 | 23 | export const sarifTemplate = { 24 | $schema: 'http://json.schemastore.org/sarif-' + sarifSchemeVersion, 25 | version: sarifSchemeVersion, 26 | runs: [ ] 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sarif-ts", 3 | "version": "0.2.0", 4 | "description": "TypeScript SARIF API", 5 | "type": "commonjs", 6 | "main": "./dist/index.js", 7 | "types": "./dist/index.d.js", 8 | "license": "MIT", 9 | "engines": { 10 | "node": ">=18.0" 11 | }, 12 | "scripts": { 13 | "build": "tsc" 14 | }, 15 | "devDependencies": { 16 | "@types/node": "^20.11.28", 17 | "@typescript-eslint/eslint-plugin": "^7.2.0", 18 | "@typescript-eslint/parser": "^7.2.0", 19 | "eslint": "^8.34.0", 20 | "typescript": "^5.0.0" 21 | }, 22 | "files": [ 23 | "README.md", 24 | "package.json", 25 | "package-lock.json", 26 | "dist/index.js" 27 | ], 28 | "keywords": [ 29 | "sarif", 30 | "document", 31 | "findings" 32 | ], 33 | "dependencies": { 34 | "ajv": "^8.13.0", 35 | "ajv-formats": "^3.0.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/examples/hello.ts: -------------------------------------------------------------------------------- 1 | import { SarifGenerator } from "../sarif/generator.js"; 2 | import { makeSourceLocation, makeBinaryLocation } from "../sarif/utils.js"; 3 | 4 | // Usage Example 5 | const sg = new SarifGenerator(); 6 | const sarifRun = sg.appendRun("MyLinter", "1.0.0"); 7 | sarifRun.addRules(); 8 | sarifRun.addErrorForRule(); 9 | sg.addError("Missing semicolon", { fileUri: "main.js", lineNumber: 10, columnNumber: 5 }); 10 | sg.addWarning("Potential code smell", { fileUri: "utils.ts", lineNumber: 25 }); 11 | sg.addNote("More tests are needed here", makeSourceLocation("tests.js", 10, 5)); 12 | sg.addError("Deprecated API call", makeBinaryLocation("MainApp", "0x12345678", 0x4546, 4)); 13 | 14 | const sarifDoc = sg.getSarif(); 15 | if (sarifDoc instanceof Error) { 16 | console.log(sarifDoc.message); 17 | } else { 18 | // Pretty-printed SARIF document 19 | console.log(JSON.stringify(sarifDoc, null, 2)); 20 | } -------------------------------------------------------------------------------- /doc/PLAN.md: -------------------------------------------------------------------------------- 1 | # PLAN 2 | 3 | * Load a Sarif Driver 4 | * name, product and semantic version 5 | * contain all rule ids 6 | * ids are unique per each driver 7 | 8 | * Load Sarif Document 9 | * Have multiple runs (array of tool+results) 10 | * each tool have a driver associated 11 | * the array of results must contain the list of findings 12 | 13 | * Saving Sarif Documents 14 | * Store each finding on the associated driver block 15 | 16 | # r2sarif 17 | 18 | The plugin must allow to do the following actions 19 | 20 | * drivers 21 | * import 22 | * from sarif document 23 | * from sarif driver document 24 | * list 25 | * select 26 | * export 27 | * finding results 28 | * those are associated with the drivers 29 | * export to file 30 | 31 | # Sarif 32 | 33 | ## 1 34 | 35 | - platform genera sarif 36 | - los clientes lo pueden leer 37 | - no se sabe que tool ha generado cada finding (la tool es platform) 38 | 39 | 40 | ## 2 41 | 42 | - los clientes pueden subir sarif custom 43 | - generar sarif con r2 44 | 45 | 46 | ### random 47 | 48 | - Visualizar findings en r2 49 | - plugin de r2 50 | - plugin de iaito / web 51 | - documentacion 52 | - corriendo en localhost del cliente 53 | - Visualizar en platform 54 | - webassembly 55 | - Visualizar en workstation 56 | - Importar ruleIds de varios sources 57 | -------------------------------------------------------------------------------- /src/sarif/utilsgen.ts: -------------------------------------------------------------------------------- 1 | import { SourceLineLocation, SourceLocation, BinaryLocation } from "./types"; 2 | 3 | export function makeSourceLocation(fileUri: string, lineNumber: number, columnNumber?: number): SourceLineLocation | SourceLocation { 4 | if (columnNumber === undefined) { 5 | return { 6 | fileUri, 7 | lineNumber, 8 | }; 9 | } 10 | return { 11 | fileUri, 12 | lineNumber, 13 | columnNumber 14 | }; 15 | } 16 | 17 | export function makeBinaryLocation(fileUri: string, memoryAddress: string, offset: number, size: number): BinaryLocation { 18 | return { 19 | physicalLocation: { 20 | artifactLocation: { 21 | uri: "binary://" + fileUri, 22 | uriBaseId: "%SRCROOT%" 23 | }, 24 | region: { 25 | byteOffset: offset, 26 | byteLength: size 27 | } 28 | }, 29 | properties: { 30 | memoryAddress 31 | } 32 | }; 33 | } 34 | 35 | export function tabulateText(columns: string[], columnWidths: number[]) : string { 36 | let resultingText = ""; 37 | let col = 0; 38 | for (const column of columns) { 39 | const columnWidth = columnWidths[col++]; 40 | resultingText += column.padEnd(columnWidth); 41 | } 42 | return resultingText.replace(/\s+$/, ''); 43 | } 44 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PLUGDIR=$(shell r2 -H R2_USER_PLUGINS) 2 | SYS_PLUGDIR=$(shell r2 -H R2_LIBR_PLUGINS) 3 | 4 | all: sarif.r2.js 5 | 6 | r2: sarif.r2.js 7 | @echo "sarif load test/bins/files/sarif.driver.ns.json" 8 | @echo "sarif addw" 9 | r2 -i sarif.r2.js /bin/ls 10 | 11 | clean: 12 | rm -f sarif.r2.js src/types.js 13 | 14 | vs: 15 | open -a "Visual Studio Code" . 16 | 17 | sarif.r2.js: src/plugin.r2.ts 18 | cd src/sarif && R2PM_OFFLINE=1 r2pm -r r2frida-compile -o types.js types.ts 19 | cd src && R2PM_OFFLINE=1 r2pm -r r2frida-compile -o ../sarif.r2.js plugin.r2.ts 20 | 21 | test: 22 | cd test && R2R_OFFLINE=1 r2r -i db 23 | 24 | citest: 25 | R2R_OFFLINE=1 r2r test/db 26 | 27 | user-install: sarif.r2.js 28 | mkdir -p $(PLUGDIR) 29 | rm -f $(PLUGDIR)/sarif.r2.js 30 | cp -f sarif.r2.js $(PLUGDIR)/sarif.r2.js 31 | 32 | user-symstall: 33 | mkdir -p $(PLUGDIR) 34 | rm -f $(PLUGDIR)/sarif.r2.js 35 | ln -fs $(shell pwd)/sarif.r2.js $(PLUGDIR)/sarif.r2.js 36 | 37 | user-uninstall: 38 | rm -f $(shell r2 -H R2_USER_PLUGINS)/sarif.r2.js 39 | 40 | install: 41 | $(MAKE) user-install PLUGDIR=$(SYS_PLUGDIR) 42 | 43 | symstall: 44 | $(MAKE) user-symstall PLUGDIR=$(SYS_PLUGDIR) 45 | 46 | uninstall: 47 | $(MAKE) user-uninstall PLUGDIR=$(SYS_PLUGDIR) 48 | 49 | purge: user-uninstall uninstall 50 | 51 | .PHONY: all test 52 | .PHONY: user-install user-symstall user-uninstall 53 | .PHONY: install symstall uninstall purge 54 | -------------------------------------------------------------------------------- /test/bins/files/sarif.rules.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/sarif-2.1.0", 3 | "version": "2.1.0", 4 | "runs": [ 5 | { 6 | "tool": { 7 | "driver": { 8 | "name": "test-rules", 9 | "semanticVersion": "1.0.0", 10 | "rules": [ 11 | { 12 | "id": "VULN-OVERFLOW", 13 | "name": "vuln-overflow", 14 | "defaultConfiguration": { 15 | "level": "warning" 16 | }, 17 | "fullDescription": { 18 | "text": "[VULN-1] Potential Buffer Overflow" 19 | } 20 | }, 21 | { 22 | "id": "VULN-WEAK-CRYPTO", 23 | "name": "vuln-weak-crypto", 24 | "defaultConfiguration": { 25 | "level": "warning" 26 | }, 27 | "fullDescription": { 28 | "text": "[VULN-1] Use of weak crypto" 29 | } 30 | }, 31 | { 32 | "id": "VULN-SQL-INJECTION", 33 | "name": "vuln-sql-injection", 34 | "defaultConfiguration": { 35 | "level": "warning" 36 | }, 37 | "fullDescription": { 38 | "text": "[VULN-1] SQL Injection Vulnerability" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /src/examples/test-sarif.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json", 3 | "version": "2.1.0", 4 | "runs": [ 5 | { 6 | "tool": { 7 | "driver": { 8 | "name": "radare2", 9 | "semanticVersion": "1.0.0", 10 | "rules": [ 11 | { 12 | "id": "EXAMPLE-VULN-001", 13 | "shortDescription": { 14 | "text": "Potential buffer overflow." 15 | }, 16 | "helpUri": "http://example.com/vulnerability/EXAMPLE-VULN-001" 17 | } 18 | ] 19 | } 20 | }, 21 | "results": [ 22 | { 23 | "ruleId": "EXAMPLE-VULN-001", 24 | "level": "error", 25 | "message": { 26 | "text": "Buffer overflow vulnerability detected." 27 | }, 28 | "locations": [ 29 | { 30 | "physicalLocation": { 31 | "artifactLocation": { 32 | "uri": "binary://example-binary", 33 | "uriBaseId": "%SRCROOT%" 34 | }, 35 | "region": { 36 | "byteOffset": 1024, 37 | "byteLength": 128 38 | } 39 | }, 40 | "properties": { 41 | "memoryAddress": "0x0040321A" 42 | } 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /test/bins/files/sarif.rules.ghidra.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/sarif-2.1.0", 3 | "version": "2.1.0", 4 | "runs": [ 5 | { 6 | "tool": { 7 | "driver": { 8 | "name": "Ghidra", 9 | "version": "11.0.1", 10 | "informationUri": "https://github.com/NationalSecurityAgency/ghidra", 11 | "rules": [ 12 | { 13 | "id": "COMMENTS", 14 | "name": "comment", 15 | "defaultConfiguration": { 16 | "level": "NONE", 17 | "kind": "INFORMATION" 18 | }, 19 | "fullDescription": { 20 | "text": "[COMMENT]" 21 | } 22 | }, 23 | { 24 | "id": "BOOKMARKS", 25 | "name": "bookmark", 26 | "defaultConfiguration": { 27 | "level": "NONE", 28 | "kind": "INFORMATION" 29 | }, 30 | "fullDescription": { 31 | "text": "[BOOKMARK]" 32 | } 33 | }, 34 | { 35 | "id": "CODE", 36 | "name": "code", 37 | "defaultConfiguration": { 38 | "level": "NONE", 39 | "kind": "INFORMATIONAL" 40 | }, 41 | "fullDescription": { 42 | "text": "[CODE]" 43 | } 44 | } 45 | ] 46 | } 47 | } 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /test/bins/files/sarif.driver.ns.json: -------------------------------------------------------------------------------- 1 | { 2 | "driver": { 3 | "name": "SARIF Findings", 4 | "product": "Platform", 5 | "semanticVersion": "0.0.1", 6 | "rules": [ 7 | { 8 | "id": "SF00001", 9 | "deprecatedIds": [ 10 | "unprotected_context_registered_broadcast_receivers" 11 | ], 12 | "name": "Context Registered Broadcast Receivers Not Protected with Permissions", 13 | "defaultConfiguration": { 14 | "level": "error" 15 | }, 16 | "properties": { 17 | "security-severity": "4", 18 | "security-severity-vector": "CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N", 19 | "tags": [ 20 | "security" 21 | ] 22 | } 23 | }, 24 | { 25 | "id": "SF00002", 26 | "deprecatedIds": [ 27 | "unprotected_manifest_broadcast_receivers" 28 | ], 29 | "name": "Manifest Declared Broadcast Receivers Not Protected With Permissions Can Leak Data to Other Apps", 30 | "defaultConfiguration": { 31 | "level": "error" 32 | }, 33 | "properties": { 34 | "security-severity": "4", 35 | "security-severity-vector": "CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N", 36 | "tags": [ 37 | "security" 38 | ] 39 | } 40 | }, 41 | { 42 | "id": "SF00003", 43 | "deprecatedIds": [ 44 | "exported_components" 45 | ], 46 | "name": "Implicitly Exported Components Block Installation to Android 12", 47 | "defaultConfiguration": { 48 | "level": "note" 49 | } 50 | } 51 | ], 52 | "isComprehensive": true, 53 | "language": "en" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | R2V: 5.9.2 5 | 6 | on: 7 | push: 8 | branches: 9 | # - master 10 | # pull_request: 11 | # schedule: 12 | # - cron: '0 0 * * 1' 13 | 14 | jobs: 15 | linux: 16 | name: linux 17 | runs-on: ubuntu-22.04 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | - name: Installing radare2 22 | run: | 23 | wget https://github.com/radareorg/radare2/releases/download/${{env.R2V}}/radare2_${{env.R2V}}_amd64.deb 24 | wget https://github.com/radareorg/radare2/releases/download/${{env.R2V}}/radare2-dev_${{env.R2V}}_amd64.deb 25 | sudo dpkg -i radare2_${{env.R2V}}_amd64.deb 26 | sudo dpkg -i radare2-dev_${{env.R2V}}_amd64.deb 27 | - name: Installing r2frida 28 | run: | 29 | wget https://github.com/nowsecure/r2frida/releases/download/${{env.R2V}}/r2frida_${{env.R2V}}_amd64.deb 30 | sudo dpkg -i r2frida_${{env.R2V}}_amd64.deb 31 | - name: Building 32 | run: make 33 | # commented because requires r2-5.9.3 34 | # - name: Running tests 35 | # run: r2r test/db 36 | - uses: actions/upload-artifact@v4 37 | with: 38 | name: sarif.r2.js 39 | path: sarif.r2.js 40 | macos: 41 | name: macos 42 | runs-on: macos-latest 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v4 46 | - name: Installing radare2 47 | run: | 48 | git clone --depth=1 https://github.com/radareorg/radare2 49 | radare2/sys/install.sh > /dev/null 50 | - name: Installing r2frida 51 | run: | 52 | r2pm -Uci r2frida > /dev/null 53 | - name: Building 54 | run: make 55 | - name: Running tests 56 | run: make citest 57 | # do not upload twice 58 | # - uses: actions/upload-artifact@v4 59 | # with: 60 | # name: sarif.r2.js 61 | # path: sarif.r2.js 62 | -------------------------------------------------------------------------------- /src/sarif/utilsverify.ts: -------------------------------------------------------------------------------- 1 | // ajv is written in commonjs, so it requires 2 | 3 | import { SourceLineLocation, SourceLocation, BinaryLocation } from "./types"; 4 | 5 | import { readFileSync as rfs } from "fs"; 6 | import { Ajv2020, ErrorObject, ValidateFunction } from "ajv/dist/2020" 7 | import ajv from "ajv" 8 | import addFormats from "ajv-formats"; 9 | 10 | // move those files into the `assets/` directory 11 | // const sarifSchemaJson = "sarif-schema-2.1.0.json"; 12 | const sarifSchema210 = "sarif-schema-2.1.0.json"; 13 | const sarifSchema220 = "sarif-schema-2.2.0.json"; 14 | 15 | export const readFileSync = rfs; 16 | 17 | export class SarifVerifier { 18 | private validateDocument: ValidateFunction; 19 | private draft06validator() : ValidateFunction{ 20 | const verifier = new ajv(); 21 | addFormats(verifier); 22 | const draft6MetaSchema = require("ajv/dist/refs/json-schema-draft-06.json") 23 | verifier.addMetaSchema(draft6MetaSchema) 24 | const sarifSchema = JSON.parse(readFileSync(sarifSchema210, "utf-8")); 25 | this.validateDocument = verifier.compile(sarifSchema); 26 | return verifier.compile(sarifSchema); 27 | } 28 | private v2020validator() : ValidateFunction{ 29 | // seems like the latest **official** standard is 2.1, 2.2 have some issues 30 | const verifier = new Ajv2020({validateSchema: false}); 31 | addFormats(verifier); 32 | const sarifSchema = JSON.parse(readFileSync(sarifSchema220, "utf-8")); 33 | return verifier.compile(sarifSchema); 34 | } 35 | constructor() { 36 | this.validateDocument= this.draft06validator(); 37 | // this.validateDocument= this.v2020validator(); 38 | } 39 | validate(sarif: any) : true | Array{ 40 | const result = this.validateDocument(sarif) 41 | if (result === true) { 42 | return true; 43 | } 44 | return this.validateDocument.errors as Array; 45 | } 46 | } 47 | 48 | 49 | export function verifySarif(sarif: any): true | Array { 50 | const sv = new SarifVerifier(); 51 | return sv.validate(sarif); 52 | } 53 | -------------------------------------------------------------------------------- /test/bins/files/sarif.rules.mastg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/sarif-2.1.0", 3 | "version": "2.1.0", 4 | "runs": [ 5 | { 6 | "tool": { 7 | "driver": { 8 | "name": "mastg", 9 | "semanticVersion": "1.0.0", 10 | "rules": [ 11 | { 12 | "id": "rules.mastg-android-insecure-random-use", 13 | "name": "rules.mastg-android-insecure-random-use", 14 | "defaultConfiguration": { 15 | "level": "warning" 16 | }, 17 | "fullDescription": { 18 | "text": "[MASVS-CRYPTO-1] The application makes use of insecure random number generator." 19 | }, 20 | "help": { 21 | "markdown": "[MASVS-CRYPTO-1] The application makes use of insecure random number generator.", 22 | "text": "[MASVS-CRYPTO-1] The application makes use of insecure random number generator." 23 | }, 24 | "properties": { 25 | "precision": "very-high", 26 | "tags": [] 27 | }, 28 | "shortDescription": { 29 | "text": "Semgrep Finding: rules.mastg-android-insecure-random-use" 30 | } 31 | }, 32 | { 33 | "id": "rules.mastg-android-non-random-use", 34 | "name": "rules.mastg-android-non-random-use", 35 | "defaultConfiguration": { 36 | "level": "warning" 37 | }, 38 | "fullDescription": { 39 | "text": "[MASVS-CRYPTO-1] The application makes use of non-random sources." 40 | }, 41 | "help": { 42 | "markdown": "[MASVS-CRYPTO-1] The application makes use of non-random sources.", 43 | "text": "[MASVS-CRYPTO-1] The application makes use of non-random sources." 44 | }, 45 | "properties": { 46 | "precision": "very-high", 47 | "tags": [] 48 | }, 49 | "shortDescription": { 50 | "text": "Semgrep Finding: rules.mastg-android-non-random-use" 51 | } 52 | } 53 | ] 54 | } 55 | } 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /src/sarif/parser.ts: -------------------------------------------------------------------------------- 1 | import { SarifDocument, Result, Location } from "./types.js"; // Assuming sarif-types.ts defines the interfaces 2 | 3 | export class SarifParser { 4 | public parse(sarifString: string): SarifDocument | Error { 5 | const obj: any = JSON.parse(sarifString); 6 | if (typeof obj["driver"] === "object") { 7 | return this.parseDriver(sarifString); 8 | } 9 | return this.parseDocument(sarifString) 10 | } 11 | 12 | public parseDocument(sarifString: string): SarifDocument | Error { 13 | try { 14 | const sarifDoc: SarifDocument = JSON.parse(sarifString); 15 | const res = this.validateSarif(sarifDoc); // Add validation (optional) 16 | if (res instanceof Error) { 17 | return res; 18 | } 19 | return sarifDoc; 20 | } catch (err) { 21 | return err; 22 | } 23 | } 24 | 25 | public parseDriver(sarifString: string) : SarifDocument | Error { 26 | const driverObject = JSON.parse(sarifString); 27 | const doc : SarifDocument = { 28 | version: "2.1.0", 29 | runs: [{ 30 | "tool": driverObject 31 | }] 32 | }; 33 | return this.parseDocument(JSON.stringify(doc)); 34 | } 35 | 36 | private validateSarif(sarifDoc: SarifDocument): Error | undefined { 37 | if (!sarifDoc.version || !sarifDoc.runs) { 38 | return new Error("SARIF document missing required properties (version or runs)"); 39 | } 40 | 41 | // Validate runs and results structure (basic) 42 | sarifDoc.runs.forEach((run) => { 43 | if (!run.tool || !run.tool.driver || !run.tool.driver.name || !run.tool.driver.version) { 44 | return new Error("SARIF run missing required tool information"); 45 | } 46 | if (run.results) { 47 | run.results.forEach((result) => { 48 | if (!result.level || !result.message) { 49 | return new Error("SARIF result missing required level or message"); 50 | } 51 | // You can add more specific checks for location format (optional) 52 | }); 53 | } 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/db/cmd/cmd_sarif: -------------------------------------------------------------------------------- 1 | NAME=sarif world 2 | FILE=- 3 | ARGS=-i ../sarif.r2.js 4 | CMDS=< sarif? 32 | sarif add [L] [id] [M] - add a new result with selected driver 33 | sarif alias [newalias] - create an alias for the sarif command 34 | sarif export - export added rules as sarif json 35 | sarif help - show this help message (-h) 36 | sarif list [help] - list drivers, rules and results 37 | sarif load [file] - import sarif info from given file 38 | sarif r2 - generate r2 script to import current doc results 39 | sarif reset - unload all documents 40 | sarif select [N] - select the nth driver 41 | sarif unload [N] - unload the nth document 42 | sarif version - show plugin version 43 | [0x00000000]> 44 | ``` 45 | 46 | First you need to load the drivers with their rules, so you can load them as 47 | flags and comments into your r2 session and use them to create your 48 | 49 | ``` 50 | [0x00000000]> sarif load rules.json 51 | ``` 52 | 53 | Those can be listed with `sarif list [what]` and use `sarif select` to pick 54 | which document you want the rest of commands to use. 55 | 56 | ## Creating your own sarif document 57 | 58 | * Seek to the offset where the vulnerability is spotted 59 | * `s 0x802180490` 60 | * Add a warning note in the current offset associated with a comment 61 | * `sarif addw SF01010 Do not use this API` 62 | 63 | You can now export the sarif file in json using the following command: 64 | 65 | ``` 66 | [0x00000000]> sarif dump > sarif.report.json 67 | [0x00000000]> sarif r2 > sarif.script.r2 68 | ``` 69 | 70 | Alternatively you can combine multiple finding documents and load that info inside r2: 71 | 72 | ``` 73 | [0x00000000]> sarif load report0.json 74 | [0x00000000]> sarif load report1.json 75 | [0x00000000]> .sarif r2 76 | ``` 77 | 78 | You will have flags prefixed with `sarif.` to spot them in the binary. `f~^sarif` 79 | 80 | ## Reference Links 81 | 82 | * https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning 83 | * https://github.com/microsoft/sarif-tutorials/ 84 | * https://docs.oasis-open.org/sarif/sarif/v2.0/sarif-v2.0.html 85 | * https://sarifweb.azurewebsites.net/#Specification 86 | * https://github.blog/2024-02-14-fixing-security-vulnerabilities-with-ai/ 87 | -------------------------------------------------------------------------------- /test/bins/files/sarif.results.r2sarif.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/sarif-2.1.0", 3 | "version": "2.1.0", 4 | "runs": [ 5 | { 6 | "properties": { 7 | "name": "radare2", 8 | "version": "5.9.1" 9 | }, 10 | "tool": { 11 | "driver": { 12 | "name": "NowSecure SARIF Findings", 13 | "semanticVersion": "0.0.1", 14 | "rules": [ 15 | { 16 | "id": "EXAMPLE-VULN-001", 17 | "name": "Buffer Overflow", 18 | "shortDescription": { 19 | "text": "Potential buffer overflow." 20 | }, 21 | "helpUri": "http://example.com/vulnerability/EXAMPLE-VULN-001" 22 | } 23 | ] 24 | } 25 | }, 26 | "results": [ 27 | { 28 | "ruleId": "EXAMPLE-VULN-001", 29 | "level": "error", 30 | "message": { 31 | "text": "Buffer overflow vulnerability detected." 32 | }, 33 | "locations": [ 34 | { 35 | "physicalLocation": { 36 | "artifactLocation": { 37 | "uri": "binary://example-binary", 38 | "uriBaseId": "%SRCROOT%" 39 | }, 40 | "region": { 41 | "byteOffset": 1024, 42 | "byteLength": 128 43 | } 44 | }, 45 | "properties": { 46 | "memoryAddress": "0x0040321A" 47 | } 48 | } 49 | ], 50 | "codeFlows": [ 51 | { 52 | "threadFlows": [ 53 | { 54 | "id": "302", 55 | "message": { 56 | "text": "location_id: \"302\"" 57 | }, 58 | "locations": [ 59 | { 60 | "module": "app", 61 | "location": { 62 | "message": { 63 | "text": "@0x100ade70c " 64 | }, 65 | "physicalLocation": { 66 | "artifactLocation": { 67 | "uri": "MyApp.app/MyApp", 68 | "uriBaseId": "APPPAYLOAD" 69 | }, 70 | "address": { 71 | "absoluteAddress": 4306364172 72 | }, 73 | "region": { 74 | "byteOffset": 173836 75 | } 76 | }, 77 | "properties": { 78 | "memoryAddress": "0x100ade70c" 79 | } 80 | } 81 | }, 82 | { 83 | "module": "system", 84 | "location": { 85 | "message": { 86 | "text": "@0x1b4cdf090 " 87 | }, 88 | "physicalLocation": { 89 | "artifactLocation": { 90 | "uri": "usr/lib/swift/libswift_Concurrency.dylib", 91 | "uriBaseId": "DEVROOT" 92 | }, 93 | "address": { 94 | "absoluteAddress": 7328362640 95 | }, 96 | "region": { 97 | "byteOffset": 192656 98 | } 99 | }, 100 | "properties": { 101 | "memoryAddress": "0x1b4cdf090" 102 | } 103 | } 104 | } 105 | ] 106 | } 107 | ] 108 | } 109 | ] 110 | } 111 | ] 112 | } 113 | ] 114 | } 115 | -------------------------------------------------------------------------------- /dist/debian/deb.mk: -------------------------------------------------------------------------------- 1 | # Create .deb without using dpkg tools. 2 | # 3 | # Author: Tim Wegener 4 | # 5 | # Use 'include deb_hand.mak' after defining the user variables in a local 6 | # makefile. 7 | # 8 | # The 'data' rule must be customised in the local make file. 9 | # This rule should make a 'data' directory containing the full file 10 | # layout of the installed package. 11 | # 12 | # This makefile will create a debian-binary file a control directory and a 13 | # a build directory in the current directory. 14 | # Do 'make clobber' to remove these generated files. 15 | # 16 | # Destination: 17 | # PACKAGE_DIR - directory where package (and support files) will be built 18 | # defaults to the current directory 19 | # 20 | # Sources: 21 | # SOURCE_DIR - directory containing files to be packaged 22 | # ICON_SOURCE - 26x26 icon file for maemo 23 | # DESCR - description with summary on first line 24 | # preinst, postinst, prerm, postrm - optional control shell scripts 25 | 26 | # These fields are used to build the control file: 27 | # PACKAGE = 28 | # VERSION = 29 | # ARCH = 30 | # SECTION = 31 | # PRIORITY = 32 | # MAINTAINER = 33 | # DEPENDS = 34 | # 35 | # SOURCE_DIR = 36 | # ICON_SOURCE = 37 | # (ICON_SOURCE is optional) 38 | 39 | # *** NO USER CHANGES REQUIRED BEYOND THIS POINT *** 40 | ifeq ($(shell uname),Darwin) 41 | MD5SUM=md5 42 | else 43 | MD5SUM=md5sum 44 | endif 45 | 46 | GAWK=awk 47 | PACKAGE_DIR=$(shell pwd) 48 | CONTROL_EXTRAS ?= ${wildcard preinst postinst prerm postrm} 49 | 50 | ${PACKAGE_DIR}/control: ${PACKAGE_DIR}/data ${CONTROL_EXTRAS} DESCR \ 51 | ${ICON_SOURCE} 52 | #rm -rf $@ 53 | mkdir -p $@ 54 | ifneq (${CONTROL_EXTRAS},) 55 | cp ${CONTROL_EXTRAS} $@ 56 | endif 57 | # Make control file. 58 | echo "Package: ${PACKAGE}" > $@/control 59 | echo "Version: ${VERSION}" >> $@/control 60 | echo "Section: ${SECTION}" >> $@/control 61 | echo "Priority: ${PRIORITY}" >> $@/control 62 | echo "Architecture: ${ARCH}" >> $@/control 63 | ifneq (${DEPENDS},) 64 | echo "Depends: ${DEPENDS}" >> $@/control 65 | endif 66 | echo "Installed-Size: ${shell du -s ${PACKAGE_DIR}/data|cut -f1}" \ 67 | >> $@/control 68 | echo "Maintainer: ${MAINTAINER}" >> $@/control 69 | printf "Description:" >> $@/control 70 | cat DESCR | ${GAWK} '{print " "$$0;}' >> $@/control 71 | #ifneq (${ICON_SOURCE},) 72 | # echo "Maemo-Icon-26:" >> $@/control 73 | # base64 ${ICON_SOURCE} | ${GAWK} '{print " "$$0;}' >> $@/control 74 | #endif 75 | # Make md5sums. 76 | cd ${PACKAGE_DIR}/data && find . -type f -exec ${MD5SUM} {} \; \ 77 | | sed -e 's| \./||' \ 78 | > $@/md5sums 79 | 80 | ${PACKAGE_DIR}/debian-binary: 81 | echo "2.0" > $@ 82 | 83 | ${PACKAGE_DIR}/clean: 84 | rm -rf ${PACKAGE_DIR}/data ${PACKAGE_DIR}/control ${PACKAGE_DIR}/build *.deb 85 | 86 | ${PACKAGE_DIR}/build: ${PACKAGE_DIR}/debian-binary ${PACKAGE_DIR}/control \ 87 | ${PACKAGE_DIR}/data 88 | rm -rf $@ 89 | mkdir $@ 90 | cp ${PACKAGE_DIR}/debian-binary $@/ 91 | cd ${PACKAGE_DIR}/control && tar czvf $@/control.tar.gz * 92 | cd ${PACKAGE_DIR}/data && \ 93 | COPY_EXTENDED_ATTRIBUTES_DISABLE=true \ 94 | COPYFILE_DISABLE=true \ 95 | tar cpzvf $@/data.tar.gz * 96 | 97 | # Convert GNU ar to BSD ar that debian requires. 98 | # Note: Order of files within ar archive is important! 99 | ${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb: ${PACKAGE_DIR}/build 100 | ar -rc $@ $ $@fail 102 | #rm -f $@tmp 103 | #mv $@fail $@ 104 | 105 | .PHONY: data 106 | data: ${PACKAGE_DIR}/data 107 | 108 | .PHONY: control 109 | control: ${PACKAGE_DIR}/control 110 | 111 | .PHONY: build 112 | build: ${PACKAGE_DIR}/build 113 | 114 | .PHONY: clean 115 | clean: ${PACKAGE_DIR}/clean $(EXTRA_CLEAN) 116 | rm -f debian-binary 117 | 118 | .PHONY: deb 119 | deb: ${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb 120 | 121 | 122 | clobber:: 123 | rm -rf ${PACKAGE_DIR}/debian_binary ${PACKAGE_DIR}/control \ 124 | ${PACKAGE_DIR}/data ${PACKAGE_DIR}/build 125 | 126 | push: 127 | scp *.deb radare.org:/srv/http/radareorg/cydia/debs 128 | 129 | mrproper: clean 130 | rm -rf root 131 | -------------------------------------------------------------------------------- /src/sarif/generator.ts: -------------------------------------------------------------------------------- 1 | import { Location, Run, Rule, Result, ResultLevel, SarifDocument, SarifError, Driver } from "./types.js"; 2 | import { sarifSchemeVersion, sarifTemplate } from "./template.js"; 3 | // import { makeSourceLocation, makeBinaryLocation } from "./utilsgen.js"; 4 | 5 | export class SarifRun { 6 | toolName: string; 7 | toolVersion?: string; 8 | toolSemanticVersion?: string; 9 | 10 | constructor(toolName: string, toolVersion?: string, toolSemanticVersion?: string) { 11 | this.toolName = toolName; 12 | } 13 | toString(): string { 14 | return "run"; 15 | } 16 | asRun(): Run { 17 | return { tool: { driver: { name: this.toolName, semanticVersion: "", version: "", rules: [] } }, results: [] }; 18 | } 19 | } 20 | 21 | function newResult(level: ResultLevel, ruleId: string, message: string, location: Location): Result { 22 | return { level: level, kind: "pass", ruleId: ruleId, "message": { text: message }, locations: [location] }; 23 | } 24 | 25 | export class SarifGenerator { 26 | private doc: SarifDocument; 27 | private currentDriver: Driver | null = null; 28 | 29 | constructor() { 30 | this.doc = sarifTemplate; 31 | } 32 | 33 | appendRun() : Error | null { 34 | if (this.currentDriver === null) { 35 | return new SarifError("No driver selected"); 36 | } 37 | const newRun: Run = { 38 | tool: { 39 | driver: { 40 | name: this.currentDriver.name, 41 | semanticVersion: this.currentDriver.semanticVersion, 42 | version: this.currentDriver.version, 43 | rules: [] 44 | } 45 | }, 46 | results: [] 47 | } 48 | this.doc.runs.push(newRun); 49 | return null; 50 | } 51 | 52 | selectDriver(driver: Driver) { 53 | this.currentDriver = driver; 54 | } 55 | 56 | addResult(result: Result) { 57 | if (this.currentDriver === null) { 58 | r2.error("No driver selected, so we can't add a result"); 59 | return; 60 | } 61 | if (this.doc.runs.length == 0) { 62 | this.appendRun(); 63 | } 64 | const lastRun = this.doc.runs[this.doc.runs.length-1]; 65 | const ruleId = result.ruleId; 66 | let validRule : Rule | null = null; 67 | for (const rule of this.currentDriver.rules) { 68 | if (rule.id === ruleId) { 69 | validRule = rule; 70 | break; 71 | } 72 | } 73 | if (validRule === null) { 74 | r2.error("Invalid ruleId"); 75 | return; 76 | } 77 | let registeredRule = false; 78 | for (const rule of lastRun.tool.driver.rules) { 79 | if (rule.id === validRule.id) { 80 | registeredRule = true; 81 | break; 82 | } 83 | } 84 | if (!registeredRule) { 85 | lastRun.tool.driver.rules.push(validRule); 86 | } 87 | // if last run was made with the same driver no need to add it again 88 | // this.doc.runs[this.runs.length-1].results.push(result); 89 | if (lastRun && lastRun.results) { 90 | lastRun.results.push(result); 91 | } else { 92 | r2.error("No run to add error"); 93 | } 94 | } 95 | 96 | addError(ruleId: string, message: string, location: Location) { 97 | this.addResult(newResult("error", ruleId, message, location)); 98 | } 99 | addWarning(ruleId: string, message: string, location: Location) { 100 | this.addResult(newResult("warning", ruleId, message, location)); 101 | } 102 | addNote(ruleId: string, message: string, location: Location) { 103 | this.addResult(newResult("note", ruleId, message, location)); 104 | } 105 | 106 | getSarif(): SarifDocument | SarifError { 107 | if (!this.doc || this.doc.runs.length === 0) { 108 | return new SarifError("This SARIF document have no results"); 109 | } 110 | return this.doc; 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /test/db/cmd/sarif_add: -------------------------------------------------------------------------------- 1 | NAME=sarif add bad args 2 | FILE=- 3 | ARGS=-i ../sarif.r2.js 4 | CMDS=<; 9 | export interface Result { 10 | ruleId: string; 11 | level: ResultLevel; 12 | kind?: string; 13 | message: ResultMessage; 14 | locations: Location[]; // (optional) file, line, column information 15 | codeFlows?: CodeFlow[]; // (optional) 16 | relatedLocations?: Location[]; // (optional) additional locations 17 | properties?: any; 18 | } 19 | 20 | // it's ugly that typescript can't enforce this at the language level 21 | const levels = ["error", "warning", "note"]; 22 | export type ResultLevel = (typeof levels)[number]; 23 | export function isValidLevel(k: string) : boolean { 24 | return levels.indexOf(k) !== -1; 25 | } 26 | 27 | const kinds = [ "notApplicable", "pass", "fail", "review", "open", "informational" ]; 28 | export type ResultKind = (typeof kinds)[number]; 29 | export function isValidKind(k: string) : boolean { 30 | return kinds.indexOf(k) !== -1; 31 | } 32 | 33 | export interface SourceLocation { 34 | fileUri: string; 35 | lineNumber: number; 36 | columnNumber: number; 37 | } 38 | 39 | export class SarifError extends Error { 40 | constructor(message: string) { 41 | super(message); 42 | this.name = "SarifError"; // this.constructor.name; 43 | } 44 | } 45 | 46 | export interface BinaryLocation { 47 | message?: string; 48 | physicalLocation: BinaryPhysicalLocation; 49 | properties: BinaryLocationProperties; 50 | } 51 | 52 | export type Location = SourceLocation | BinaryLocation | SourceLineLocation; 53 | 54 | export interface ThreadFlowMessage { 55 | text: string; 56 | } 57 | export interface ThreadFlowLocation { 58 | module: string; 59 | message: ThreadFlowMessage; 60 | location: BinaryLocation; 61 | // physicalLocation: BinaryPhysicalLocation; 62 | // properties: any; // memoryAddress: string 63 | } 64 | 65 | export interface ThreadFlow { 66 | id: string; 67 | message: ThreadFlowMessage; 68 | locations: ThreadFlowLocation[]; 69 | } 70 | 71 | export interface CodeFlow { 72 | threadFlows: ThreadFlow[] 73 | } 74 | 75 | export interface BinaryLocationProperties { 76 | memoryAddress: string; 77 | } 78 | 79 | export type SourceLineLocation = Partial< 80 | Pick & 81 | Pick & 82 | { columnNumber: 0 }>; 83 | 84 | export interface BinaryArtifactLocation { 85 | uri: string; // "binary://example-binary", 86 | uriBaseId?: string; // "%SRCROOT%" 87 | } 88 | 89 | export interface BinaryPhysicalLocationAddress { 90 | absoluteAddress: number; 91 | } 92 | 93 | export interface BinaryPhysicalLocation { 94 | artifactLocation: BinaryArtifactLocation; 95 | address?: BinaryPhysicalLocationAddress; 96 | region: BinaryRegionLocation; 97 | } 98 | 99 | export interface BinaryRegionLocation { 100 | byteOffset: number; 101 | byteLength: number; 102 | } 103 | 104 | export interface Driver { 105 | name: string; 106 | version?: string; // e.g., "0.3-beta4" 107 | semanticVersion?: string; // e.g., "2.4.0" 108 | rules: Rule[]; 109 | } 110 | 111 | export interface Tool { 112 | driver: Driver 113 | } 114 | 115 | /* 116 | export interface SarifResult { 117 | // {"ruleId":"EXAMPLE-VULN-001","level":"error","message":{"text":"Buffer overflow vulnerability detected."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"binary://example-binary","uriBaseId":"%SRCROOT%"},"region":{"byteOffset":1024,"byteLength":128}},"properties":{"memoryAddress":"0x0040321A"}}]}]} 118 | ruleId: string; 119 | level: ResultLevel; 120 | message: { 121 | text:string; 122 | }; 123 | locations: SarifLocation[]; 124 | } 125 | */ 126 | 127 | export interface ShortDescription { 128 | text: string // "Potential buffer overflow." 129 | } 130 | 131 | export interface SarifRule { 132 | driver :Driver; 133 | rule: Rule; 134 | } 135 | 136 | export interface Rule { 137 | "id": string; // "EXAMPLE-VULN-001", 138 | "name": string, 139 | "level": string, 140 | "shortDescription": ShortDescription | undefined, 141 | "fullDescription": ShortDescription | undefined, 142 | "helpUri": string, // "http://example.com/vulnerability/EXAMPLE-VULN-001" 143 | } 144 | 145 | export interface Run { 146 | tool: Tool; 147 | results?: Result[]; 148 | } 149 | 150 | export interface SarifDocument { 151 | version: string; // e.g., "2.4.0" 152 | runs: Run[]; 153 | } 154 | 155 | 156 | -------------------------------------------------------------------------------- /src/sarif/index.ts: -------------------------------------------------------------------------------- 1 | // XXX UNUSED CODE 2 | // XXX we can remove this file entirely 3 | 4 | import { sarifTemplate } from "./template.js"; 5 | import { Rule, SarifDocument, SarifError } from "./types.js"; 6 | 7 | class Sarif { 8 | doc: any; 9 | rulesLoaded: Map; 10 | 11 | constructor() { 12 | this.doc = sarifTemplate; 13 | this.rulesLoaded = new Map; 14 | } 15 | 16 | /* load rules from a sarif document */ 17 | loadRules(sarifDocument: any) { 18 | // unused 19 | for (const run of sarifDocument.runs) { 20 | // maybe this should be a warning.. and loadingRules should return a LoadingReport instead 21 | if (!run.tool || !run.tool.driver || !run.tool.driver.rules) { 22 | // return new SarifError("Missing driver rules"); 23 | continue; 24 | } 25 | for (const rule of run.tool.driver.rules) { 26 | this.rulesLoaded.set(rule.id, rule); 27 | } 28 | } 29 | } 30 | 31 | loadResults(sarifDocument: any) : Error | undefined { 32 | // unused 33 | const r2baddr = 0; 34 | for (const run of sarifDocument.runs) { 35 | let baddr = 0; 36 | if (!run.artifacts) { 37 | continue; 38 | } 39 | for (const art of run.artifacts) { 40 | // console.log(art.location.uri); 41 | // console.log(art.sourceLanguage); 42 | // console.log(art.properties.additionalProperties); 43 | baddr = parseInt('0x' + art.properties.additionalProperties.imageBase); 44 | } 45 | for (const res of run.results) { 46 | const ruleId = res.ruleId; 47 | const level = res.level; 48 | let message = ''; 49 | if (res && res.properties && res.properties.additionalProperties) { 50 | message = res.properties.additionalProperties.value; 51 | } else { 52 | message = res.message.text; 53 | } 54 | try { 55 | const flows = res.codeFlows; 56 | console.log(flows); 57 | } catch (e) { 58 | console.error(e); 59 | // ignore 60 | } 61 | const loc0 = res.locations[0]; 62 | try { 63 | // console.log(JSON.stringify(loc0, null, 2)); 64 | const phyloc = loc0.physicalLocation; 65 | const artifact = (phyloc && phyloc.artifactLocation) ? phyloc.artifactLocation.uri : ''; 66 | const locations = []; 67 | if (loc0 && loc0.properties && loc0.properties.memoryAddress) { 68 | locations.push({ 69 | va: loc0.properties.memoryAddress, 70 | pa: phyloc.region.byteOffset, 71 | sz: phyloc.region.byteLength 72 | }); 73 | } else { 74 | const pa = phyloc.address.absoluteAddress; 75 | const sz = phyloc.address.length; 76 | let va = pa; 77 | if (baddr > 0) { 78 | if (va >= baddr) { 79 | va -= baddr; 80 | } 81 | va += r2baddr; 82 | } 83 | locations.push({ 84 | va: va, 85 | sz: sz, 86 | pa: pa 87 | }); 88 | } 89 | this.addResult(ruleId, level, message, artifact, locations); 90 | } catch (e) { 91 | return e; 92 | } 93 | } 94 | } 95 | } 96 | 97 | addRule(id) { 98 | if (this.doc.runs[0].tool.driver.rules.filter((x) => x.id === id).length !== 0) { 99 | return true; 100 | } 101 | const rule = this.rulesLoaded[id]; 102 | if (rule) { 103 | this.doc.runs[0].tool.driver.rules.push(rule); 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | reset() { 110 | this.doc.runs[0].results = []; 111 | this.doc.runs[0].tool.driver.rules = []; 112 | } 113 | 114 | addResult(ruleId, level, message, artifact, locations) : Error | undefined { 115 | if (!this.addRule(ruleId)) { 116 | return new Error ('Invalid rule id: ' + ruleId); 117 | } 118 | const sarifLocations = []; 119 | const result = { 120 | ruleId: ruleId, 121 | level: level, 122 | message: { 123 | text: message 124 | }, 125 | locations: [] 126 | }; 127 | const locationTemplate = { 128 | physicalLocation: { 129 | artifactLocation: { 130 | uri: 'binary://' + artifact, 131 | uriBaseId: '%SRCROOT%' 132 | }, 133 | region: { 134 | byteOffset: locations, 135 | byteLength: 128 136 | } 137 | }, 138 | properties: { 139 | memoryAddress: '0x0040321A' 140 | } 141 | }; 142 | for (const loc of locations) { 143 | const myLoc = locationTemplate; 144 | myLoc.physicalLocation.region = { 145 | byteOffset: loc.pa, 146 | byteLength: loc.sz 147 | }; 148 | myLoc.properties = { 149 | memoryAddress: loc.va 150 | }; 151 | result.locations.push(myLoc); 152 | } 153 | this.doc.runs[0].results.push(result); 154 | } 155 | 156 | /** 157 | * Converts the SARIF document to a JSON string with 2 spaces of indentation. 158 | * @returns {string} The SARIF document as a JSON string. 159 | */ 160 | toString() { 161 | return JSON.stringify(this.doc, null, 2) + '\n'; 162 | } 163 | 164 | /** 165 | * Generates a Radare2 script from the SARIF document. 166 | * The script includes comments and flags for the identified issues in the SARIF document. 167 | * @returns {string} The Radare2 script. 168 | */ 169 | toRadareScript() { 170 | let script = '# r2sarif script\n'; 171 | const results = this.doc.runs[0].results; 172 | let counter = 0; 173 | for (const res of results) { 174 | let text = ''; 175 | if (res && res.properties && res.properties.additionalProperties) { 176 | text = res.properties.additionalProperties.value; 177 | } else { 178 | text = res.message.text; 179 | } 180 | for (const loc of res.locations) { 181 | const address = loc.properties.memoryAddress; 182 | const size = loc.physicalLocation.region.byteLength; 183 | const ruleId = res.ruleId; 184 | // script += `CC ${ruleId}:${text} @ ${address}\n`; 185 | const addr = address; 186 | script += `# ${text} @ ${addr}\n`; 187 | // TODO: detect when there are two comments in the same address 188 | if (res.ruleId === 'COMMENTS') { 189 | const comment = `${text}`; 190 | script += `CC ${comment} @ ${address}\n`; 191 | } else { 192 | const comment = `${ruleId}:${text}`; 193 | script += `CC ${comment} @ ${address}\n`; 194 | script += `f sarif.${counter} ${size} ${address}\n`; 195 | } 196 | counter++; 197 | } 198 | } 199 | return script; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /Attic/sarif.r2.js: -------------------------------------------------------------------------------- 1 | var sarifRegisterPlugin = (function () { 2 | const sarifTemplate = { 3 | $schema: 'http://json.schemastore.org/sarif-2.1.0', 4 | version: '2.1.0', 5 | runs: [ 6 | { 7 | tool: { 8 | driver: { 9 | name: 'radare2', 10 | semanticVersion: '1.0.0', 11 | rules: [ 12 | ] 13 | } 14 | }, 15 | results: [ 16 | ] 17 | } 18 | ] 19 | }; 20 | 21 | class R2Sarif { 22 | constructor () { 23 | this.doc = sarifTemplate; 24 | this.rulesLoaded = {}; 25 | } 26 | 27 | /* load rules from a sarif file */ 28 | loadRules (sarifDocument) { 29 | for (const run of sarifDocument.runs) { 30 | try { 31 | if (!run.tool || !run.tool.driver || !run.tool.driver.rules) { 32 | continue; 33 | } 34 | for (const rule of run.tool.driver.rules) { 35 | this.rulesLoaded[rule.id] = rule; 36 | } 37 | } catch (e) { 38 | console.error('loadRules', e); 39 | } 40 | } 41 | } 42 | 43 | loadResults (sarifDocument) { 44 | const r2baddr = parseInt(r2.cmd('e bin.baddr')); 45 | for (const run of sarifDocument.runs) { 46 | let baddr = 0; 47 | if (!run.artifacts) { 48 | continue; 49 | } 50 | for (const art of run.artifacts) { 51 | // console.log(art.location.uri); 52 | // console.log(art.sourceLanguage); 53 | // console.log(art.properties.additionalProperties); 54 | baddr = parseInt('0x' + art.properties.additionalProperties.imageBase); 55 | } 56 | for (const res of run.results) { 57 | const ruleId = res.ruleId; 58 | const level = res.level; 59 | let message = ''; 60 | if (res && res.properties && res.properties.additionalProperties) { 61 | message = res.properties.additionalProperties.value; 62 | } else { 63 | message = res.message.text; 64 | } 65 | const loc0 = res.locations[0]; 66 | try { 67 | // console.log(JSON.stringify(loc0, null, 2)); 68 | const phyloc = loc0.physicalLocation; 69 | const artifact = (phyloc && phyloc.artifactLocation) ? phyloc.artifactLocation.uri : ''; 70 | const locations = []; 71 | if (loc0 && loc0.properties && loc0.properties.memoryAddress) { 72 | locations.push({ 73 | va: loc0.properties.memoryAddress, 74 | pa: phyloc.region.byteOffset, 75 | sz: phyloc.region.byteLength 76 | }); 77 | } else { 78 | const pa = phyloc.address.absoluteAddress; 79 | const sz = phyloc.address.length; 80 | let va = pa; 81 | if (baddr > 0) { 82 | if (va >= baddr) { 83 | va -= baddr; 84 | } 85 | va += r2baddr; 86 | } 87 | locations.push({ 88 | va: va, 89 | sz: sz, 90 | pa: pa 91 | }); 92 | } 93 | this.addResult(ruleId, level, message, artifact, locations); 94 | } catch (e) { 95 | console.error(e); 96 | } 97 | } 98 | } 99 | } 100 | 101 | addRule (id) { 102 | if (this.doc.runs[0].tool.driver.rules.filter((x) => x.id === id).length !== 0) { 103 | return true; 104 | } 105 | const rule = this.rulesLoaded[id]; 106 | if (rule) { 107 | this.doc.runs[0].tool.driver.rules.push(rule); 108 | return true; 109 | } 110 | return false; 111 | } 112 | 113 | reset () { 114 | this.doc.runs[0].results = []; 115 | this.doc.runs[0].tool.driver.rules = []; 116 | } 117 | 118 | addResult (ruleId, level, message, artifact, locations) { 119 | if (!this.addRule(ruleId)) { 120 | console.error('Invalid rule id: ' + ruleId); 121 | return false; 122 | } 123 | const sarifLocations = []; 124 | const result = { 125 | ruleId: ruleId, 126 | level: level, 127 | message: { 128 | text: message 129 | }, 130 | locations: [] 131 | }; 132 | const locationTemplate = { 133 | physicalLocation: { 134 | artifactLocation: { 135 | uri: 'binary://' + artifact, 136 | uriBaseId: '%SRCROOT%' 137 | }, 138 | region: { 139 | byteOffset: locations, 140 | byteLength: 128 141 | } 142 | }, 143 | properties: { 144 | memoryAddress: '0x0040321A' 145 | } 146 | }; 147 | for (const loc of locations) { 148 | const myLoc = locationTemplate; 149 | myLoc.physicalLocation.region = { 150 | byteOffset: loc.pa, 151 | byteLength: loc.sz 152 | }; 153 | myLoc.properties = { 154 | memoryAddress: loc.va 155 | }; 156 | result.locations.push(myLoc); 157 | } 158 | this.doc.runs[0].results.push(result); 159 | return true; 160 | } 161 | 162 | toString () { 163 | return JSON.stringify(this.doc, null, 2) + '\n'; 164 | } 165 | 166 | toScript () { 167 | let script = '# r2sarif script\n'; 168 | const results = this.doc.runs[0].results; 169 | let counter = 0; 170 | for (const res of results) { 171 | let text = ''; 172 | if (res && res.properties && res.properties.additionalProperties) { 173 | text = res.properties.additionalProperties.value; 174 | } else { 175 | text = res.message.text; 176 | } 177 | for (const loc of res.locations) { 178 | const address = loc.properties.memoryAddress; 179 | const size = loc.physicalLocation.region.byteLength; 180 | const ruleId = res.ruleId; 181 | // script += `CC ${ruleId}:${text} @ ${address}\n`; 182 | const addr = r2.cmd(`?v ${address}`).trim(); 183 | script += `# ${text} @ ${addr}\n`; 184 | // TODO: detect when there are two comments in the same address 185 | if (res.ruleId === 'COMMENTS') { 186 | const comment = `${text}`; 187 | script += `CC ${comment} @ ${address}\n`; 188 | } else { 189 | const comment = `${ruleId}:${text}`; 190 | script += `CC ${comment} @ ${address}\n`; 191 | script += `f sarif.${counter} ${size} ${address}\n`; 192 | } 193 | counter++; 194 | } 195 | } 196 | return script; 197 | } 198 | } 199 | 200 | function sarifTest () { 201 | const s = new R2Sarif(); 202 | s.addResultOverflow('/bin/ls', 'buffer overflow detected', [ 203 | { va: 0x804804, pa: 0x804, sz: 32 } 204 | ]); 205 | console.log(s.toString()); 206 | console.log(s.toScript()); 207 | } 208 | 209 | function sarifRegisterPlugin () { 210 | const sarif = new R2Sarif(); 211 | function sarifCommand (args) { 212 | function sarifHelp () { 213 | console.log('sarif [action] [arguments]'); 214 | console.log('sarif -h, help - show this help message (-h)'); 215 | console.log('sarif -a, add [r] [c] - add a new sarif finding'); 216 | console.log('sarif -aw,-ae,-an [r] [c] - add warning, error or note'); 217 | console.log('sarif -i, import [file] - import sarif info from given file'); 218 | console.log('sarif -e, export [file] - export sarif findings into given file or stdout'); 219 | console.log('sarif -r, r2|script - generate r2 script with loaded sarif info'); 220 | console.log('sarif -R, reset - reset reported findings list'); 221 | console.log('sarif -l, rules ([file]) - list or load rules from file'); 222 | } 223 | function sarifLoadRules (fileName) { 224 | const sarifObject = r2.cmdj(`cat ${fileName}`); 225 | sarif.loadRules(sarifObject); 226 | } 227 | function sarifLoadResults (fileName) { 228 | const sarifObject = r2.cmdj(`cat ${fileName}`); 229 | sarif.loadRules(sarifObject); 230 | sarif.loadResults(sarifObject); 231 | } 232 | function listRules () { 233 | const res = []; 234 | for (const ruleId of Object.keys(sarif.rulesLoaded)) { 235 | const rule = sarif.rulesLoaded[ruleId]; 236 | res.push(`- ${ruleId}`); 237 | try { 238 | const desc = rule.fullDescription.text; 239 | res.push(` - description: ${desc}`); 240 | } catch (e) {} 241 | try { 242 | const level = rule.defaultConfiguration.level; 243 | res.push(` - level: ${level}`); 244 | } catch (e) {} 245 | } 246 | return res.join('\n'); 247 | } 248 | function sarifImport (fileName) { 249 | if (fileName === '') { 250 | console.log('Usage: sarif -i [filename]'); 251 | } else { 252 | sarifLoadResults(fileName); 253 | } 254 | } 255 | function sarifExport () { 256 | console.log(sarif.toString()); 257 | } 258 | function sarifScript (fileName) { 259 | r2.log(sarif.toScript()); 260 | } 261 | function sarifAdd (level, args) { 262 | const arg = args.split(/ /); 263 | if (arg.length === 0) { 264 | console.error('Usage: sarif add[?] [id] [message]'); 265 | return false; 266 | } 267 | const artifact = r2.cmd('o.').trim(); 268 | const loc0 = { 269 | va: +r2.cmd('?v $$'), 270 | pa: r2.cmd('?p $$').trim(), 271 | sz: 1 272 | }; 273 | const locations = [loc0]; 274 | const ruleId = arg[0]; 275 | const rule = sarif.rulesLoaded[ruleId]; 276 | const comment = arg.length > 1 ? arg[1] : ''; 277 | if (level === null) { 278 | try { 279 | level = rule.defaultConfiguration.level; 280 | } catch (err) { 281 | level = 'warning'; 282 | } 283 | } 284 | if (!sarif.addResult(ruleId, level, comment, artifact, locations)) { 285 | console.error('Cannot add result'); 286 | } 287 | } 288 | let arg = args.substr('sarif'.length).trim(); 289 | const space = arg.indexOf(' '); 290 | let action = arg.trim(); 291 | if (space !== -1) { 292 | action = arg.substr(0, space); 293 | arg = arg.substr(space + 1); 294 | } else { 295 | arg = ''; 296 | } 297 | switch (action) { 298 | case '': 299 | case '?': 300 | case '-h': 301 | case 'help': 302 | sarifHelp(); 303 | break; 304 | case '-a': 305 | case 'add': 306 | sarifAdd(null, arg); 307 | break; 308 | case '-aw': 309 | case 'addw': 310 | sarifAdd('warning', arg); 311 | break; 312 | case '-ae': 313 | case 'adde': 314 | sarifAdd('error', arg); 315 | break; 316 | case '-an': 317 | case 'addn': 318 | sarifAdd('note', arg); 319 | break; 320 | case '-l': 321 | case 'lr': 322 | case 'rules': 323 | case 'load-rules': 324 | if (arg) { 325 | sarifLoadRules(arg); 326 | } else { 327 | r2.log(listRules()); 328 | } 329 | break; 330 | case '-i': 331 | case 'import': 332 | sarifImport(arg); 333 | break; 334 | case '-j': 335 | case '-e': 336 | case 'export': 337 | sarifExport(arg); 338 | break; 339 | case '*': 340 | case '-r': 341 | case 'r2': 342 | case 'script': 343 | sarifScript(arg); 344 | break; 345 | case '-R': 346 | case 'reset': 347 | sarif.reset(); 348 | break; 349 | default: 350 | console.error('Unknown action'); 351 | break; 352 | } 353 | } 354 | r2.unload('core', 'sarif'); 355 | r2.plugin('core', function () { 356 | function coreCall (cmd) { 357 | if (cmd.startsWith('sarif')) { 358 | try { 359 | sarifCommand(cmd); 360 | } catch (e) { 361 | console.error(e); 362 | } 363 | return true; 364 | } 365 | return false; 366 | } 367 | return { 368 | name: 'sarif', 369 | license: 'MIT', 370 | desc: 'support importing and exporting sarif format', 371 | call: coreCall 372 | }; 373 | }); 374 | } 375 | return sarifRegisterPlugin; 376 | })(); 377 | sarifRegisterPlugin(); 378 | -------------------------------------------------------------------------------- /src/plugin.r2.ts: -------------------------------------------------------------------------------- 1 | import { StringMap, SarifDocument, Driver, ResultKind, BinaryLocation, ResultLevel, Rule, Result, isValidLevel, isValidKind } from "./sarif/types.js"; 2 | import { SarifGenerator, SarifRun } from "./sarif/generator.js"; 3 | import { tabulateText } from "./sarif/utilsgen.js"; 4 | import { SarifParser } from "./sarif/parser.js" 5 | 6 | const pluginName = "sarif"; 7 | const pluginVersion = "0.0.1"; 8 | 9 | interface R2Pipe { 10 | unload(module: string, name: string): void; 11 | plugin(module: string, def: any): void; 12 | cmd(command: string): string; 13 | log(msg: string); 14 | error(msg: string); 15 | } 16 | 17 | declare global { 18 | var r2: R2Pipe; 19 | function b64(string): string; 20 | } 21 | 22 | function readFileSync(fileName: string): string | Error { 23 | try { 24 | const data = r2.cmd("cat " + fileName); 25 | return data.trim(); 26 | } catch (e) { 27 | return e; 28 | } 29 | } 30 | 31 | const sarifListHelp = `sarif list [type] # see 'sarif select' 32 | | docs list all loaded documents 33 | | drivers list all drivers available 34 | | json merge and dump all sarif documents together in json format 35 | | r2 list results from all documents loaded as r2 commands 36 | | results list all the results/findings from loaded documents 37 | | rules list rules from all documents or the selected one 38 | `.trim(); 39 | 40 | class R2Sarif { 41 | version: string = pluginVersion; 42 | sf: SarifParser; 43 | paths: string[] = []; 44 | docs: SarifDocument[] = []; 45 | currentDriver: Driver | null = null; 46 | currentDriverIndex: number = -1; 47 | alias: string | null = null; 48 | currentDocument: SarifGenerator; 49 | currentRun: SarifRun; 50 | 51 | constructor() { 52 | this.sf = new SarifParser(); 53 | this.alias = null; 54 | this.currentDocument = new SarifGenerator(); 55 | } 56 | load() { 57 | try { 58 | const res = this.sf.parse("{}"); 59 | if (res instanceof Error) { 60 | r2.error("Cannot parse this sarif document"); 61 | r2.error(res.toString()); 62 | } else { 63 | r2.error("ok"); 64 | } 65 | } catch (err) { 66 | r2.error(err); 67 | } 68 | } 69 | 70 | unloadSarif(args: string[]): boolean { 71 | if (args.length === 1) { 72 | const documentIndex = parseInt(args[0]); 73 | let newDocs: SarifDocument[] = []; 74 | let newPaths: string[] = []; 75 | let count = 0; 76 | for (const doc of this.docs) { 77 | if (count++ === documentIndex) { 78 | r2.error("Document " + count + " unloaded. Run 'sarif select' again please."); 79 | } else { 80 | newDocs.push(doc); 81 | newPaths.push(this.paths[count]); 82 | } 83 | } 84 | this.docs = newDocs; 85 | this.paths = newPaths; 86 | this.selectDriver(-1); 87 | } 88 | return true; 89 | } 90 | 91 | toString(): string { 92 | const sarif = this.currentDocument.getSarif(); 93 | if (sarif instanceof Error) { 94 | r2.error(sarif.toString()); 95 | return ""; 96 | } 97 | return JSON.stringify(sarif, null, 2); 98 | } 99 | 100 | toScript(): string { 101 | const sarif = this.currentDocument.getSarif(); 102 | if (sarif instanceof Error) { 103 | r2.error(sarif.toString()); 104 | return ""; 105 | } 106 | const s = this.currentDocument.getSarif(); 107 | if (s instanceof Error) { 108 | r2.error(s.toString()); 109 | return ""; 110 | } 111 | const script: string[] = []; 112 | script.push("# SARIF script for radare2"); 113 | let counter = 0; 114 | for (const run of s.runs) { 115 | if (run.results) { 116 | run.results.forEach((res) => { 117 | // script.push(JSON.stringify(res, null, 4)); 118 | for (const loc of res.locations) { 119 | if ((loc as BinaryLocation).properties) { 120 | const bloc = loc as BinaryLocation; 121 | // r2.log(loc.physicalLocation.artifactLocation.uri); 122 | const addr = bloc.properties.memoryAddress; 123 | const size = bloc.physicalLocation.region.byteLength; 124 | const rule = res.ruleId; 125 | let text; 126 | if (res && res.properties && res.properties.additionalProperties) { 127 | text = res.properties.additionalProperties.value; 128 | } else { 129 | text = res.message.text; 130 | } 131 | const at = r2.cmd(`?v ${addr}`).trim(); 132 | script.push(`# ${text} @ ${at} / ${size} ${rule}`); 133 | script.push(`CC ${rule}:${text} @ ${at}`); // encode in base64 134 | script.push(`f sarif.${counter} ${size} ${at}`); 135 | } 136 | } 137 | }); 138 | } 139 | } 140 | return script.join("\n"); 141 | } 142 | 143 | loadSarif(args: string[]): boolean { 144 | if (args.length === 1) { 145 | const [fileName] = args; 146 | const data = readFileSync(fileName) 147 | if (data instanceof Error) { 148 | r2.error("Error: " + data); 149 | return false; 150 | } 151 | const doc = this.sf.parse(data); 152 | if (doc instanceof Error) { 153 | r2.error("Error parsing " + doc); 154 | return false; 155 | } 156 | this.docs.push(doc); 157 | this.paths.push(fileName); 158 | this.selectDriver(this.listDrivers(true).length - 1); 159 | r2.error("Document loaded and driver selected. Use 'sarif list'") 160 | } else { 161 | r2.error("Usage: sarif load "); 162 | } 163 | return true; 164 | } 165 | 166 | selectDriver(index: number): boolean { 167 | if (index === -1) { 168 | this.currentDriverIndex = -1; 169 | this.currentDriver = null; 170 | return true; 171 | } 172 | const drivers = this.listDrivers(true); 173 | if (index < drivers.length) { 174 | this.currentDriver = drivers[index]; 175 | this.currentDriverIndex = index; 176 | r2.log("Selected driver:") 177 | this.listDriver(index, this.currentDriver); 178 | this.currentDocument.selectDriver(this.currentDriver); 179 | return true; 180 | } 181 | r2.error("Invalid index. Run: sarif list drivers"); 182 | return false; 183 | } 184 | 185 | listDriver(index: number, driver: Driver) { 186 | const sel = (index === this.currentDriverIndex) ? "* " : " "; 187 | const ver = driver.semanticVersion ? driver.semanticVersion : driver.version ? driver.version : "" 188 | const text = tabulateText([sel, index.toString(), driver.name, ver], [2, 4, 20, 20]); 189 | r2.log(text); 190 | } 191 | 192 | listDocs() { 193 | let count = 0; 194 | for (const doc of this.docs) { 195 | const docPath = this.paths[count]; 196 | r2.log(count + " " + docPath); 197 | for (const run of doc.runs) { 198 | r2.log(" + " + run.tool.driver.name + " " + run.tool.driver.semanticVersion); 199 | } 200 | count++; 201 | } 202 | } 203 | 204 | listDrivers(silent?: boolean): Driver[] { 205 | var res: Driver[] = []; 206 | let count = 0; 207 | for (const doc of this.docs) { 208 | for (const run of doc.runs) { 209 | const driver = run.tool.driver; 210 | const driverCount = count++; 211 | if (!silent) { 212 | this.listDriver(driverCount, driver); 213 | } 214 | res.push(driver); 215 | } 216 | } 217 | return res; 218 | } 219 | listJson() { 220 | for (const doc of this.docs) { 221 | r2.log(JSON.stringify(doc)); 222 | } 223 | } 224 | 225 | getRulesAsMap(): StringMap { 226 | const myMap: StringMap = new Map(); 227 | const rules = this.listRules(true); 228 | for (const rule of rules) { 229 | if (myMap.get(rule.id)) { 230 | r2.error("Duplicated RuleID: " + rule.id); 231 | } 232 | // const desc = rule.shortDescription?.text ?? rule.fullDescription?.text ?? ""; 233 | myMap.set(rule.id, rule.name); 234 | } 235 | return myMap; 236 | } 237 | 238 | getAllResults() : Result[] { 239 | var allResults: Result[] = []; 240 | for (const doc of this.docs) { 241 | for (const run of doc.runs) { 242 | if (run.results) { 243 | for (const res of run.results) { 244 | allResults.push(res); 245 | } 246 | } 247 | } 248 | } 249 | return allResults; 250 | } 251 | 252 | listResultsAsR2() { 253 | var res: Result[] = []; 254 | const ruleMap = this.getRulesAsMap(); 255 | const results = this.getAllResults(); 256 | const script : string[] = []; 257 | // TODO Find base address of all the artifacts involved 258 | for (const res of results) { 259 | if (!res.locations && !res.codeFlows) { 260 | console.error("This result has no locations or codeFlows " + res.ruleId); 261 | continue; 262 | } 263 | let addr = ""; 264 | if (res.locations) { 265 | const bloc = res.locations[0] as BinaryLocation; 266 | addr = "0x" + bloc.physicalLocation.address?.absoluteAddress.toString(16); 267 | } else if (res.codeFlows) { 268 | for (const cf of res.codeFlows) { 269 | for (const tf of cf.threadFlows) { 270 | const tfloc = cf.threadFlows[0].locations[0]; 271 | addr = "0x" + tfloc.location.physicalLocation.address?.absoluteAddress.toString(16); 272 | break; 273 | } 274 | } 275 | } 276 | const desc = ruleMap.get(res.ruleId); 277 | const comment = `${res.ruleId}: ${desc}`; 278 | script.push(`'@${addr}'CC ${comment}`); 279 | // script.push("CC base64:" + b64(comment) + " @ " + addr); 280 | 281 | if (res.message) { 282 | let message: undefined|string = undefined; 283 | if (res.message.arguments) { 284 | const args = res.message.arguments; 285 | const arg0 = args[0]; 286 | args.shift(); 287 | const argText = args.join(", "); 288 | message = `${arg0}(${argText})`; 289 | } else if (res.message.text) { 290 | message = res.message.text; 291 | } 292 | if (message) { 293 | // script.push("CC base64:" + b64(message) + " @ " + addr); 294 | script.push(`'@${addr}'CC ${message}`); 295 | } 296 | } 297 | const codeflow : string[] = []; 298 | if (res.codeFlows !== undefined) { 299 | for (const cf of res.codeFlows) { 300 | for (const tf of cf.threadFlows) { 301 | for (const tfloc of tf.locations) { 302 | let addr = "0x" + tfloc.location.physicalLocation.address?.absoluteAddress.toString(16); 303 | codeflow.push(addr); 304 | } 305 | } 306 | } 307 | } 308 | if (codeflow.length > 0) { 309 | script.push("'abt+" + codeflow.join(" ")); 310 | } 311 | } 312 | r2.log(script.join("\n")); 313 | } 314 | listResults(): Result[] { 315 | var res: Result[] = []; 316 | const ruleMap = this.getRulesAsMap(); 317 | const results = this.getAllResults(); 318 | for (const res of results) { 319 | let resultText = res.ruleId; 320 | const desc = ruleMap.get(res.ruleId); 321 | if (desc !== undefined) { 322 | resultText += " :: " + desc; 323 | } 324 | r2.log(resultText); 325 | if (res.message) { 326 | if (res.message.arguments) { 327 | const args = res.message.arguments; 328 | const arg0 = args[0]; 329 | args.shift(); 330 | r2.log(" :: " + arg0 + " (" + args.join(", ") + ")"); 331 | } else if (res.message.text) { 332 | r2.log(" :: " + res.message.text); 333 | } 334 | } 335 | if (res.codeFlows !== undefined) { 336 | for (const cf of res.codeFlows) { 337 | for (const tf of cf.threadFlows) { 338 | for (const tfloc of tf.locations) { 339 | let addr = "0x" + tfloc.location.physicalLocation.address?.absoluteAddress.toString(16); 340 | let relAddr = tfloc.location.physicalLocation.region.byteOffset; 341 | let text = " - " + addr + " " + tfloc.module; 342 | if (tfloc.location) { 343 | const phys = tfloc.location.physicalLocation; 344 | text += " " + phys.artifactLocation.uri; 345 | text += " +" + relAddr; 346 | } 347 | r2.log(text); 348 | } 349 | } 350 | } 351 | } else if (res.locations) { 352 | for (const loc of res.locations) { 353 | const bloc = loc as BinaryLocation; 354 | let addr = "0x" + bloc.physicalLocation.address?.absoluteAddress.toString(16); 355 | let relAddr = bloc.physicalLocation.region.byteOffset; 356 | let text = " - " + addr + " module"; 357 | if (bloc.physicalLocation.region.byteOffset !== undefined) { 358 | text += " +" + relAddr.toString(); 359 | } 360 | r2.log(" - " + text); 361 | } 362 | } 363 | } 364 | return res; 365 | } 366 | listRule(rule: Rule) { 367 | const text = (rule.shortDescription 368 | ? rule.shortDescription.text 369 | : rule.fullDescription 370 | ? rule.fullDescription.text 371 | : rule.name 372 | ? rule.name : ""); 373 | const line = tabulateText([rule.id, text], [20, 40]); 374 | r2.log(line); 375 | } 376 | listRulesForDriver(driver: Driver, quiet?: boolean): Rule[] { 377 | if (quiet === true) { 378 | return driver.rules; 379 | } 380 | // r2.log("# Rules for Driver: " + driver.name + " (" + driver.semanticVersion + ")") 381 | for (const rule of driver.rules) { 382 | this.listRule(rule); 383 | } 384 | return driver.rules; 385 | } 386 | listRules(quiet? : boolean): Rule[] { 387 | if (this.currentDriver !== null) { 388 | return this.listRulesForDriver(this.currentDriver, quiet); 389 | } 390 | var res: Rule[] = []; 391 | for (const doc of this.docs) { 392 | for (const run of doc.runs) { 393 | const driver = run.tool.driver; 394 | res.push(...this.listRulesForDriver(driver)); 395 | } 396 | } 397 | return res; 398 | } 399 | 400 | reset() { 401 | this.docs = []; 402 | this.paths = []; 403 | this.currentDriver = null; 404 | this.currentDriverIndex = -1; 405 | } 406 | 407 | add(level: ResultLevel, kind: ResultKind, ruleId: string, messageText: string): boolean { 408 | if (this.currentDriver === null) { 409 | r2.log("No driver selected"); 410 | return false; 411 | } 412 | if (!isValidLevel(level)) { 413 | r2.log("Invalid result level: " + level); 414 | return false; 415 | } 416 | if (!isValidKind(kind)) { 417 | r2.log("Invalid result kind: " + kind); 418 | return false; 419 | } 420 | const rules = this.listRulesForDriver(this.currentDriver, true); 421 | const pa = Number(r2.cmd("?p $$")); 422 | const va = r2.cmd("?v $$").trim(); 423 | const sz = Number(r2.cmd("ao~size[1]")); // b")); 424 | const fileName = r2.cmd("o.").trim(); 425 | for (const rule of rules) { 426 | if (rule.id === ruleId) { 427 | const result: Result = { 428 | ruleId: ruleId, 429 | message: { 430 | text: messageText 431 | }, 432 | kind: kind, 433 | level: level, 434 | locations: [] 435 | }; 436 | const loc: BinaryLocation = { 437 | physicalLocation: { 438 | artifactLocation: { 439 | uri: "binary://" + fileName, 440 | // uriBaseId: "%SRCROOT%" 441 | }, 442 | region: { 443 | byteOffset: pa, 444 | byteLength: sz 445 | } 446 | }, 447 | properties: { 448 | memoryAddress: va.toString() 449 | } 450 | } 451 | result.locations.push(loc); 452 | this.currentDocument.addResult(result); 453 | return true; 454 | } 455 | } 456 | return false; 457 | } 458 | } 459 | 460 | function showHelp() { 461 | const println = r2.log; 462 | println(`Usage: sarif [action] [args...] 463 | sarif add [L] [id] [M] - add a new result with selected driver 464 | sarif alias [newalias] - create an alias for the sarif command 465 | sarif export - export added rules as sarif json 466 | sarif help - show this help message (-h) 467 | sarif list [help] - list drivers, rules and results 468 | sarif load [file] - import sarif info from given file 469 | sarif r2 - generate r2 script to import current doc results 470 | sarif reset - unload all documents 471 | sarif select [N] - select the nth driver 472 | sarif unload [N] - unload the nth document 473 | sarif version - show plugin version 474 | `.trim()); 475 | } 476 | 477 | function sarifCommand(r2s: R2Sarif, cmd: string): boolean { 478 | if (r2s.alias === null || !cmd.startsWith(r2s.alias)) { 479 | if (!cmd.startsWith(pluginName)) { 480 | return false; 481 | } 482 | } 483 | const args = cmd.slice(pluginName.length).trim().split(" "); 484 | if (args.length === 0) { 485 | showHelp() 486 | } else switch (args[0]) { 487 | case "": 488 | case "?": 489 | case "-h": 490 | case "help": 491 | showHelp(); 492 | break; 493 | case "-a": 494 | case "add": 495 | if (args.length >= 4) { 496 | const levelType = args[1]; 497 | const kind = args[2]; 498 | const ruleId = args[3]; 499 | const textMessage = args.slice(4).join(" "); 500 | if (!r2s.add(levelType, kind, ruleId, textMessage)) { 501 | r2.log("sarif add failed"); 502 | } 503 | } else { 504 | r2.log("sarif add [type] [kind] [ruleid] [message]") 505 | r2.log("type = warning, error, note") 506 | r2.log("kind = notApplicable, pass, fail, review, open, informational"); 507 | r2.log("ruleid = run: sarif list rules"); 508 | r2.log("message = associated comment represented as a space separated list of words"); 509 | } 510 | break; 511 | case 'addw': 512 | case '-aw': 513 | if (args.length >= 3) { 514 | const kind = args[1] 515 | const ruleId = args[2]; 516 | const textMessage = args.slice(3).join(" "); 517 | if (!r2s.add("warning", kind, ruleId, textMessage)) { 518 | r2.error("addw invalid arguments"); 519 | } 520 | } else { 521 | r2.error("sarif addw [kind] [id] [message]") 522 | } 523 | break; 524 | case 'adde': 525 | case '-ae': 526 | if (args.length >= 3) { 527 | const ruleId = args[1]; 528 | const kind = args[2]; 529 | const textMessage = args.slice(3).join(" "); 530 | r2s.add("error", kind, ruleId, textMessage); 531 | } else { 532 | r2.log("sarif adde [kind] [id] [message]") 533 | } 534 | break; 535 | case 'addn': 536 | case '-an': 537 | if (args.length >= 3) { 538 | const ruleId = args[1]; 539 | const kind = args[2]; 540 | const textMessage = args.slice(3).join(" "); 541 | r2s.add("note", kind, ruleId, textMessage); 542 | } else { 543 | r2.log("sarif addn [kind] [id] [message]") 544 | } 545 | break; 546 | case '-A': 547 | case 'alias': 548 | if (args.length === 2) { 549 | const newAlias = args[1]; 550 | if (newAlias.startsWith("-")) { 551 | r2s.alias = null; 552 | } else { 553 | r2s.alias = args[1]; 554 | } 555 | } else { 556 | if (r2s.alias) { 557 | r2.log(r2s.alias); 558 | } else { 559 | r2.error("Alias not defined"); 560 | } 561 | } 562 | break; 563 | case '-c': 564 | case "clean": 565 | r2s.reset(); 566 | break; 567 | case '-s': 568 | case 'select': 569 | if (args.length === 2) { 570 | r2s.selectDriver(Number.parseInt(args[1])); 571 | } else { 572 | r2s.listDrivers(); 573 | } 574 | break; 575 | case '-l': 576 | case "ls": 577 | case "list": 578 | if (args.length === 2) { 579 | const arg = args[1]; 580 | switch (arg) { 581 | case "-R": 582 | case "rul": 583 | case "rules": 584 | r2s.listRules(); 585 | break; 586 | case "-j": 587 | case "json": 588 | r2s.listJson(); 589 | break; 590 | case "-r": 591 | case "res": 592 | case "results": 593 | r2s.listResults(); 594 | break; 595 | case "r2": 596 | r2s.listResultsAsR2(); 597 | break; 598 | case "-d": 599 | case "drv": 600 | case "drivers": 601 | r2s.listDrivers(); 602 | break; 603 | case "docs": 604 | r2s.listDocs(); 605 | break; 606 | default: 607 | r2.log(sarifListHelp); 608 | break; 609 | } 610 | } else { 611 | r2.log(sarifListHelp); 612 | } 613 | break; 614 | case '-i': 615 | case 'import': 616 | case 'load': 617 | r2s.loadSarif(args.slice(1)); 618 | break; 619 | case "unload": 620 | r2s.unloadSarif(args.slice(1)); 621 | break; 622 | case "version": 623 | case "-V": 624 | r2.log(r2s.version); 625 | break; 626 | case "dump": 627 | r2.log(r2s.toString()); 628 | break; 629 | case "r2": 630 | r2.log(r2s.toScript()); 631 | break; 632 | } 633 | return true; 634 | } 635 | 636 | function registerSarifPlugin() { 637 | r2.unload("core", pluginName); 638 | r2.plugin("core", function () { 639 | const r2s = new R2Sarif(); 640 | function coreCall(cmd: string) { 641 | try { 642 | return sarifCommand(r2s, cmd); 643 | } catch (e) { 644 | r2.error(e.stack); 645 | r2.error(e); 646 | } 647 | } 648 | return { 649 | name: pluginName, 650 | license: "MIT", 651 | desc: "Manage SARIF documents", 652 | call: coreCall 653 | } 654 | }); 655 | } 656 | 657 | try { 658 | registerSarifPlugin(); 659 | } catch (e) { 660 | r2.error(e); 661 | r2.error(e.stack); 662 | } 663 | -------------------------------------------------------------------------------- /src/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sarif-ts", 3 | "version": "0.2.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "sarif-ts", 9 | "version": "0.2.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "ajv": "^8.13.0", 13 | "ajv-formats": "^3.0.1" 14 | }, 15 | "devDependencies": { 16 | "@types/node": "^20.11.28", 17 | "@typescript-eslint/eslint-plugin": "^7.2.0", 18 | "@typescript-eslint/parser": "^7.2.0", 19 | "eslint": "^8.34.0", 20 | "typescript": "^5.0.0" 21 | }, 22 | "engines": { 23 | "node": ">=18.0" 24 | } 25 | }, 26 | "node_modules/@aashutoshrathi/word-wrap": { 27 | "version": "1.2.6", 28 | "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", 29 | "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", 30 | "dev": true, 31 | "engines": { 32 | "node": ">=0.10.0" 33 | } 34 | }, 35 | "node_modules/@eslint-community/eslint-utils": { 36 | "version": "4.4.0", 37 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", 38 | "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", 39 | "dev": true, 40 | "dependencies": { 41 | "eslint-visitor-keys": "^3.3.0" 42 | }, 43 | "engines": { 44 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 45 | }, 46 | "peerDependencies": { 47 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 48 | } 49 | }, 50 | "node_modules/@eslint-community/regexpp": { 51 | "version": "4.10.0", 52 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", 53 | "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", 54 | "dev": true, 55 | "engines": { 56 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 57 | } 58 | }, 59 | "node_modules/@eslint/eslintrc": { 60 | "version": "2.1.4", 61 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", 62 | "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", 63 | "dev": true, 64 | "dependencies": { 65 | "ajv": "^6.12.4", 66 | "debug": "^4.3.2", 67 | "espree": "^9.6.0", 68 | "globals": "^13.19.0", 69 | "ignore": "^5.2.0", 70 | "import-fresh": "^3.2.1", 71 | "js-yaml": "^4.1.0", 72 | "minimatch": "^3.1.2", 73 | "strip-json-comments": "^3.1.1" 74 | }, 75 | "engines": { 76 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 77 | }, 78 | "funding": { 79 | "url": "https://opencollective.com/eslint" 80 | } 81 | }, 82 | "node_modules/@eslint/eslintrc/node_modules/ajv": { 83 | "version": "6.12.6", 84 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 85 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 86 | "dev": true, 87 | "dependencies": { 88 | "fast-deep-equal": "^3.1.1", 89 | "fast-json-stable-stringify": "^2.0.0", 90 | "json-schema-traverse": "^0.4.1", 91 | "uri-js": "^4.2.2" 92 | }, 93 | "funding": { 94 | "type": "github", 95 | "url": "https://github.com/sponsors/epoberezkin" 96 | } 97 | }, 98 | "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { 99 | "version": "0.4.1", 100 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 101 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 102 | "dev": true 103 | }, 104 | "node_modules/@eslint/js": { 105 | "version": "8.57.0", 106 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", 107 | "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", 108 | "dev": true, 109 | "engines": { 110 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 111 | } 112 | }, 113 | "node_modules/@humanwhocodes/config-array": { 114 | "version": "0.11.14", 115 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", 116 | "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", 117 | "dev": true, 118 | "dependencies": { 119 | "@humanwhocodes/object-schema": "^2.0.2", 120 | "debug": "^4.3.1", 121 | "minimatch": "^3.0.5" 122 | }, 123 | "engines": { 124 | "node": ">=10.10.0" 125 | } 126 | }, 127 | "node_modules/@humanwhocodes/module-importer": { 128 | "version": "1.0.1", 129 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 130 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 131 | "dev": true, 132 | "engines": { 133 | "node": ">=12.22" 134 | }, 135 | "funding": { 136 | "type": "github", 137 | "url": "https://github.com/sponsors/nzakas" 138 | } 139 | }, 140 | "node_modules/@humanwhocodes/object-schema": { 141 | "version": "2.0.2", 142 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", 143 | "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", 144 | "dev": true 145 | }, 146 | "node_modules/@nodelib/fs.scandir": { 147 | "version": "2.1.5", 148 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 149 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 150 | "dev": true, 151 | "dependencies": { 152 | "@nodelib/fs.stat": "2.0.5", 153 | "run-parallel": "^1.1.9" 154 | }, 155 | "engines": { 156 | "node": ">= 8" 157 | } 158 | }, 159 | "node_modules/@nodelib/fs.stat": { 160 | "version": "2.0.5", 161 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 162 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 163 | "dev": true, 164 | "engines": { 165 | "node": ">= 8" 166 | } 167 | }, 168 | "node_modules/@nodelib/fs.walk": { 169 | "version": "1.2.8", 170 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 171 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 172 | "dev": true, 173 | "dependencies": { 174 | "@nodelib/fs.scandir": "2.1.5", 175 | "fastq": "^1.6.0" 176 | }, 177 | "engines": { 178 | "node": ">= 8" 179 | } 180 | }, 181 | "node_modules/@types/json-schema": { 182 | "version": "7.0.15", 183 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 184 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 185 | "dev": true 186 | }, 187 | "node_modules/@types/node": { 188 | "version": "20.11.28", 189 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", 190 | "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", 191 | "dev": true, 192 | "dependencies": { 193 | "undici-types": "~5.26.4" 194 | } 195 | }, 196 | "node_modules/@types/semver": { 197 | "version": "7.5.8", 198 | "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", 199 | "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", 200 | "dev": true 201 | }, 202 | "node_modules/@typescript-eslint/eslint-plugin": { 203 | "version": "7.2.0", 204 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", 205 | "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", 206 | "dev": true, 207 | "dependencies": { 208 | "@eslint-community/regexpp": "^4.5.1", 209 | "@typescript-eslint/scope-manager": "7.2.0", 210 | "@typescript-eslint/type-utils": "7.2.0", 211 | "@typescript-eslint/utils": "7.2.0", 212 | "@typescript-eslint/visitor-keys": "7.2.0", 213 | "debug": "^4.3.4", 214 | "graphemer": "^1.4.0", 215 | "ignore": "^5.2.4", 216 | "natural-compare": "^1.4.0", 217 | "semver": "^7.5.4", 218 | "ts-api-utils": "^1.0.1" 219 | }, 220 | "engines": { 221 | "node": "^16.0.0 || >=18.0.0" 222 | }, 223 | "funding": { 224 | "type": "opencollective", 225 | "url": "https://opencollective.com/typescript-eslint" 226 | }, 227 | "peerDependencies": { 228 | "@typescript-eslint/parser": "^7.0.0", 229 | "eslint": "^8.56.0" 230 | }, 231 | "peerDependenciesMeta": { 232 | "typescript": { 233 | "optional": true 234 | } 235 | } 236 | }, 237 | "node_modules/@typescript-eslint/parser": { 238 | "version": "7.2.0", 239 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", 240 | "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", 241 | "dev": true, 242 | "dependencies": { 243 | "@typescript-eslint/scope-manager": "7.2.0", 244 | "@typescript-eslint/types": "7.2.0", 245 | "@typescript-eslint/typescript-estree": "7.2.0", 246 | "@typescript-eslint/visitor-keys": "7.2.0", 247 | "debug": "^4.3.4" 248 | }, 249 | "engines": { 250 | "node": "^16.0.0 || >=18.0.0" 251 | }, 252 | "funding": { 253 | "type": "opencollective", 254 | "url": "https://opencollective.com/typescript-eslint" 255 | }, 256 | "peerDependencies": { 257 | "eslint": "^8.56.0" 258 | }, 259 | "peerDependenciesMeta": { 260 | "typescript": { 261 | "optional": true 262 | } 263 | } 264 | }, 265 | "node_modules/@typescript-eslint/scope-manager": { 266 | "version": "7.2.0", 267 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", 268 | "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", 269 | "dev": true, 270 | "dependencies": { 271 | "@typescript-eslint/types": "7.2.0", 272 | "@typescript-eslint/visitor-keys": "7.2.0" 273 | }, 274 | "engines": { 275 | "node": "^16.0.0 || >=18.0.0" 276 | }, 277 | "funding": { 278 | "type": "opencollective", 279 | "url": "https://opencollective.com/typescript-eslint" 280 | } 281 | }, 282 | "node_modules/@typescript-eslint/type-utils": { 283 | "version": "7.2.0", 284 | "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", 285 | "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", 286 | "dev": true, 287 | "dependencies": { 288 | "@typescript-eslint/typescript-estree": "7.2.0", 289 | "@typescript-eslint/utils": "7.2.0", 290 | "debug": "^4.3.4", 291 | "ts-api-utils": "^1.0.1" 292 | }, 293 | "engines": { 294 | "node": "^16.0.0 || >=18.0.0" 295 | }, 296 | "funding": { 297 | "type": "opencollective", 298 | "url": "https://opencollective.com/typescript-eslint" 299 | }, 300 | "peerDependencies": { 301 | "eslint": "^8.56.0" 302 | }, 303 | "peerDependenciesMeta": { 304 | "typescript": { 305 | "optional": true 306 | } 307 | } 308 | }, 309 | "node_modules/@typescript-eslint/types": { 310 | "version": "7.2.0", 311 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", 312 | "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", 313 | "dev": true, 314 | "engines": { 315 | "node": "^16.0.0 || >=18.0.0" 316 | }, 317 | "funding": { 318 | "type": "opencollective", 319 | "url": "https://opencollective.com/typescript-eslint" 320 | } 321 | }, 322 | "node_modules/@typescript-eslint/typescript-estree": { 323 | "version": "7.2.0", 324 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", 325 | "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", 326 | "dev": true, 327 | "dependencies": { 328 | "@typescript-eslint/types": "7.2.0", 329 | "@typescript-eslint/visitor-keys": "7.2.0", 330 | "debug": "^4.3.4", 331 | "globby": "^11.1.0", 332 | "is-glob": "^4.0.3", 333 | "minimatch": "9.0.3", 334 | "semver": "^7.5.4", 335 | "ts-api-utils": "^1.0.1" 336 | }, 337 | "engines": { 338 | "node": "^16.0.0 || >=18.0.0" 339 | }, 340 | "funding": { 341 | "type": "opencollective", 342 | "url": "https://opencollective.com/typescript-eslint" 343 | }, 344 | "peerDependenciesMeta": { 345 | "typescript": { 346 | "optional": true 347 | } 348 | } 349 | }, 350 | "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { 351 | "version": "2.0.1", 352 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 353 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 354 | "dev": true, 355 | "dependencies": { 356 | "balanced-match": "^1.0.0" 357 | } 358 | }, 359 | "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { 360 | "version": "9.0.3", 361 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", 362 | "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", 363 | "dev": true, 364 | "dependencies": { 365 | "brace-expansion": "^2.0.1" 366 | }, 367 | "engines": { 368 | "node": ">=16 || 14 >=14.17" 369 | }, 370 | "funding": { 371 | "url": "https://github.com/sponsors/isaacs" 372 | } 373 | }, 374 | "node_modules/@typescript-eslint/utils": { 375 | "version": "7.2.0", 376 | "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", 377 | "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", 378 | "dev": true, 379 | "dependencies": { 380 | "@eslint-community/eslint-utils": "^4.4.0", 381 | "@types/json-schema": "^7.0.12", 382 | "@types/semver": "^7.5.0", 383 | "@typescript-eslint/scope-manager": "7.2.0", 384 | "@typescript-eslint/types": "7.2.0", 385 | "@typescript-eslint/typescript-estree": "7.2.0", 386 | "semver": "^7.5.4" 387 | }, 388 | "engines": { 389 | "node": "^16.0.0 || >=18.0.0" 390 | }, 391 | "funding": { 392 | "type": "opencollective", 393 | "url": "https://opencollective.com/typescript-eslint" 394 | }, 395 | "peerDependencies": { 396 | "eslint": "^8.56.0" 397 | } 398 | }, 399 | "node_modules/@typescript-eslint/visitor-keys": { 400 | "version": "7.2.0", 401 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", 402 | "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", 403 | "dev": true, 404 | "dependencies": { 405 | "@typescript-eslint/types": "7.2.0", 406 | "eslint-visitor-keys": "^3.4.1" 407 | }, 408 | "engines": { 409 | "node": "^16.0.0 || >=18.0.0" 410 | }, 411 | "funding": { 412 | "type": "opencollective", 413 | "url": "https://opencollective.com/typescript-eslint" 414 | } 415 | }, 416 | "node_modules/@ungap/structured-clone": { 417 | "version": "1.2.0", 418 | "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", 419 | "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", 420 | "dev": true 421 | }, 422 | "node_modules/acorn": { 423 | "version": "8.11.3", 424 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", 425 | "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", 426 | "dev": true, 427 | "bin": { 428 | "acorn": "bin/acorn" 429 | }, 430 | "engines": { 431 | "node": ">=0.4.0" 432 | } 433 | }, 434 | "node_modules/acorn-jsx": { 435 | "version": "5.3.2", 436 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 437 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 438 | "dev": true, 439 | "peerDependencies": { 440 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 441 | } 442 | }, 443 | "node_modules/ajv": { 444 | "version": "8.13.0", 445 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", 446 | "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", 447 | "dependencies": { 448 | "fast-deep-equal": "^3.1.3", 449 | "json-schema-traverse": "^1.0.0", 450 | "require-from-string": "^2.0.2", 451 | "uri-js": "^4.4.1" 452 | }, 453 | "funding": { 454 | "type": "github", 455 | "url": "https://github.com/sponsors/epoberezkin" 456 | } 457 | }, 458 | "node_modules/ajv-formats": { 459 | "version": "3.0.1", 460 | "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", 461 | "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", 462 | "dependencies": { 463 | "ajv": "^8.0.0" 464 | }, 465 | "peerDependencies": { 466 | "ajv": "^8.0.0" 467 | }, 468 | "peerDependenciesMeta": { 469 | "ajv": { 470 | "optional": true 471 | } 472 | } 473 | }, 474 | "node_modules/ansi-regex": { 475 | "version": "5.0.1", 476 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 477 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 478 | "dev": true, 479 | "engines": { 480 | "node": ">=8" 481 | } 482 | }, 483 | "node_modules/ansi-styles": { 484 | "version": "4.3.0", 485 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 486 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 487 | "dev": true, 488 | "dependencies": { 489 | "color-convert": "^2.0.1" 490 | }, 491 | "engines": { 492 | "node": ">=8" 493 | }, 494 | "funding": { 495 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 496 | } 497 | }, 498 | "node_modules/argparse": { 499 | "version": "2.0.1", 500 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 501 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 502 | "dev": true 503 | }, 504 | "node_modules/array-union": { 505 | "version": "2.1.0", 506 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 507 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 508 | "dev": true, 509 | "engines": { 510 | "node": ">=8" 511 | } 512 | }, 513 | "node_modules/balanced-match": { 514 | "version": "1.0.2", 515 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 516 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 517 | "dev": true 518 | }, 519 | "node_modules/brace-expansion": { 520 | "version": "1.1.11", 521 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 522 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 523 | "dev": true, 524 | "dependencies": { 525 | "balanced-match": "^1.0.0", 526 | "concat-map": "0.0.1" 527 | } 528 | }, 529 | "node_modules/braces": { 530 | "version": "3.0.3", 531 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 532 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 533 | "dev": true, 534 | "dependencies": { 535 | "fill-range": "^7.1.1" 536 | }, 537 | "engines": { 538 | "node": ">=8" 539 | } 540 | }, 541 | "node_modules/callsites": { 542 | "version": "3.1.0", 543 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 544 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 545 | "dev": true, 546 | "engines": { 547 | "node": ">=6" 548 | } 549 | }, 550 | "node_modules/chalk": { 551 | "version": "4.1.2", 552 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 553 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 554 | "dev": true, 555 | "dependencies": { 556 | "ansi-styles": "^4.1.0", 557 | "supports-color": "^7.1.0" 558 | }, 559 | "engines": { 560 | "node": ">=10" 561 | }, 562 | "funding": { 563 | "url": "https://github.com/chalk/chalk?sponsor=1" 564 | } 565 | }, 566 | "node_modules/color-convert": { 567 | "version": "2.0.1", 568 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 569 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 570 | "dev": true, 571 | "dependencies": { 572 | "color-name": "~1.1.4" 573 | }, 574 | "engines": { 575 | "node": ">=7.0.0" 576 | } 577 | }, 578 | "node_modules/color-name": { 579 | "version": "1.1.4", 580 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 581 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 582 | "dev": true 583 | }, 584 | "node_modules/concat-map": { 585 | "version": "0.0.1", 586 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 587 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 588 | "dev": true 589 | }, 590 | "node_modules/cross-spawn": { 591 | "version": "7.0.3", 592 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 593 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 594 | "dev": true, 595 | "dependencies": { 596 | "path-key": "^3.1.0", 597 | "shebang-command": "^2.0.0", 598 | "which": "^2.0.1" 599 | }, 600 | "engines": { 601 | "node": ">= 8" 602 | } 603 | }, 604 | "node_modules/debug": { 605 | "version": "4.3.4", 606 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 607 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 608 | "dev": true, 609 | "dependencies": { 610 | "ms": "2.1.2" 611 | }, 612 | "engines": { 613 | "node": ">=6.0" 614 | }, 615 | "peerDependenciesMeta": { 616 | "supports-color": { 617 | "optional": true 618 | } 619 | } 620 | }, 621 | "node_modules/deep-is": { 622 | "version": "0.1.4", 623 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 624 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 625 | "dev": true 626 | }, 627 | "node_modules/dir-glob": { 628 | "version": "3.0.1", 629 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 630 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 631 | "dev": true, 632 | "dependencies": { 633 | "path-type": "^4.0.0" 634 | }, 635 | "engines": { 636 | "node": ">=8" 637 | } 638 | }, 639 | "node_modules/doctrine": { 640 | "version": "3.0.0", 641 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 642 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 643 | "dev": true, 644 | "dependencies": { 645 | "esutils": "^2.0.2" 646 | }, 647 | "engines": { 648 | "node": ">=6.0.0" 649 | } 650 | }, 651 | "node_modules/escape-string-regexp": { 652 | "version": "4.0.0", 653 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 654 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 655 | "dev": true, 656 | "engines": { 657 | "node": ">=10" 658 | }, 659 | "funding": { 660 | "url": "https://github.com/sponsors/sindresorhus" 661 | } 662 | }, 663 | "node_modules/eslint": { 664 | "version": "8.57.0", 665 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", 666 | "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", 667 | "dev": true, 668 | "dependencies": { 669 | "@eslint-community/eslint-utils": "^4.2.0", 670 | "@eslint-community/regexpp": "^4.6.1", 671 | "@eslint/eslintrc": "^2.1.4", 672 | "@eslint/js": "8.57.0", 673 | "@humanwhocodes/config-array": "^0.11.14", 674 | "@humanwhocodes/module-importer": "^1.0.1", 675 | "@nodelib/fs.walk": "^1.2.8", 676 | "@ungap/structured-clone": "^1.2.0", 677 | "ajv": "^6.12.4", 678 | "chalk": "^4.0.0", 679 | "cross-spawn": "^7.0.2", 680 | "debug": "^4.3.2", 681 | "doctrine": "^3.0.0", 682 | "escape-string-regexp": "^4.0.0", 683 | "eslint-scope": "^7.2.2", 684 | "eslint-visitor-keys": "^3.4.3", 685 | "espree": "^9.6.1", 686 | "esquery": "^1.4.2", 687 | "esutils": "^2.0.2", 688 | "fast-deep-equal": "^3.1.3", 689 | "file-entry-cache": "^6.0.1", 690 | "find-up": "^5.0.0", 691 | "glob-parent": "^6.0.2", 692 | "globals": "^13.19.0", 693 | "graphemer": "^1.4.0", 694 | "ignore": "^5.2.0", 695 | "imurmurhash": "^0.1.4", 696 | "is-glob": "^4.0.0", 697 | "is-path-inside": "^3.0.3", 698 | "js-yaml": "^4.1.0", 699 | "json-stable-stringify-without-jsonify": "^1.0.1", 700 | "levn": "^0.4.1", 701 | "lodash.merge": "^4.6.2", 702 | "minimatch": "^3.1.2", 703 | "natural-compare": "^1.4.0", 704 | "optionator": "^0.9.3", 705 | "strip-ansi": "^6.0.1", 706 | "text-table": "^0.2.0" 707 | }, 708 | "bin": { 709 | "eslint": "bin/eslint.js" 710 | }, 711 | "engines": { 712 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 713 | }, 714 | "funding": { 715 | "url": "https://opencollective.com/eslint" 716 | } 717 | }, 718 | "node_modules/eslint-visitor-keys": { 719 | "version": "3.4.3", 720 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 721 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 722 | "dev": true, 723 | "engines": { 724 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 725 | }, 726 | "funding": { 727 | "url": "https://opencollective.com/eslint" 728 | } 729 | }, 730 | "node_modules/eslint/node_modules/ajv": { 731 | "version": "6.12.6", 732 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 733 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 734 | "dev": true, 735 | "dependencies": { 736 | "fast-deep-equal": "^3.1.1", 737 | "fast-json-stable-stringify": "^2.0.0", 738 | "json-schema-traverse": "^0.4.1", 739 | "uri-js": "^4.2.2" 740 | }, 741 | "funding": { 742 | "type": "github", 743 | "url": "https://github.com/sponsors/epoberezkin" 744 | } 745 | }, 746 | "node_modules/eslint/node_modules/eslint-scope": { 747 | "version": "7.2.2", 748 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", 749 | "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", 750 | "dev": true, 751 | "dependencies": { 752 | "esrecurse": "^4.3.0", 753 | "estraverse": "^5.2.0" 754 | }, 755 | "engines": { 756 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 757 | }, 758 | "funding": { 759 | "url": "https://opencollective.com/eslint" 760 | } 761 | }, 762 | "node_modules/eslint/node_modules/estraverse": { 763 | "version": "5.3.0", 764 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 765 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 766 | "dev": true, 767 | "engines": { 768 | "node": ">=4.0" 769 | } 770 | }, 771 | "node_modules/eslint/node_modules/json-schema-traverse": { 772 | "version": "0.4.1", 773 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 774 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 775 | "dev": true 776 | }, 777 | "node_modules/espree": { 778 | "version": "9.6.1", 779 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", 780 | "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", 781 | "dev": true, 782 | "dependencies": { 783 | "acorn": "^8.9.0", 784 | "acorn-jsx": "^5.3.2", 785 | "eslint-visitor-keys": "^3.4.1" 786 | }, 787 | "engines": { 788 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 789 | }, 790 | "funding": { 791 | "url": "https://opencollective.com/eslint" 792 | } 793 | }, 794 | "node_modules/esquery": { 795 | "version": "1.5.0", 796 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", 797 | "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", 798 | "dev": true, 799 | "dependencies": { 800 | "estraverse": "^5.1.0" 801 | }, 802 | "engines": { 803 | "node": ">=0.10" 804 | } 805 | }, 806 | "node_modules/esquery/node_modules/estraverse": { 807 | "version": "5.3.0", 808 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 809 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 810 | "dev": true, 811 | "engines": { 812 | "node": ">=4.0" 813 | } 814 | }, 815 | "node_modules/esrecurse": { 816 | "version": "4.3.0", 817 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 818 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 819 | "dev": true, 820 | "dependencies": { 821 | "estraverse": "^5.2.0" 822 | }, 823 | "engines": { 824 | "node": ">=4.0" 825 | } 826 | }, 827 | "node_modules/esrecurse/node_modules/estraverse": { 828 | "version": "5.3.0", 829 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 830 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 831 | "dev": true, 832 | "engines": { 833 | "node": ">=4.0" 834 | } 835 | }, 836 | "node_modules/esutils": { 837 | "version": "2.0.3", 838 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 839 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 840 | "dev": true, 841 | "engines": { 842 | "node": ">=0.10.0" 843 | } 844 | }, 845 | "node_modules/fast-deep-equal": { 846 | "version": "3.1.3", 847 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 848 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 849 | }, 850 | "node_modules/fast-glob": { 851 | "version": "3.3.2", 852 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", 853 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", 854 | "dev": true, 855 | "dependencies": { 856 | "@nodelib/fs.stat": "^2.0.2", 857 | "@nodelib/fs.walk": "^1.2.3", 858 | "glob-parent": "^5.1.2", 859 | "merge2": "^1.3.0", 860 | "micromatch": "^4.0.4" 861 | }, 862 | "engines": { 863 | "node": ">=8.6.0" 864 | } 865 | }, 866 | "node_modules/fast-glob/node_modules/glob-parent": { 867 | "version": "5.1.2", 868 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 869 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 870 | "dev": true, 871 | "dependencies": { 872 | "is-glob": "^4.0.1" 873 | }, 874 | "engines": { 875 | "node": ">= 6" 876 | } 877 | }, 878 | "node_modules/fast-json-stable-stringify": { 879 | "version": "2.1.0", 880 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 881 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 882 | "dev": true 883 | }, 884 | "node_modules/fast-levenshtein": { 885 | "version": "2.0.6", 886 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 887 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 888 | "dev": true 889 | }, 890 | "node_modules/fastq": { 891 | "version": "1.17.1", 892 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", 893 | "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", 894 | "dev": true, 895 | "dependencies": { 896 | "reusify": "^1.0.4" 897 | } 898 | }, 899 | "node_modules/file-entry-cache": { 900 | "version": "6.0.1", 901 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 902 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 903 | "dev": true, 904 | "dependencies": { 905 | "flat-cache": "^3.0.4" 906 | }, 907 | "engines": { 908 | "node": "^10.12.0 || >=12.0.0" 909 | } 910 | }, 911 | "node_modules/fill-range": { 912 | "version": "7.1.1", 913 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 914 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 915 | "dev": true, 916 | "dependencies": { 917 | "to-regex-range": "^5.0.1" 918 | }, 919 | "engines": { 920 | "node": ">=8" 921 | } 922 | }, 923 | "node_modules/find-up": { 924 | "version": "5.0.0", 925 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 926 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 927 | "dev": true, 928 | "dependencies": { 929 | "locate-path": "^6.0.0", 930 | "path-exists": "^4.0.0" 931 | }, 932 | "engines": { 933 | "node": ">=10" 934 | }, 935 | "funding": { 936 | "url": "https://github.com/sponsors/sindresorhus" 937 | } 938 | }, 939 | "node_modules/flat-cache": { 940 | "version": "3.2.0", 941 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", 942 | "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", 943 | "dev": true, 944 | "dependencies": { 945 | "flatted": "^3.2.9", 946 | "keyv": "^4.5.3", 947 | "rimraf": "^3.0.2" 948 | }, 949 | "engines": { 950 | "node": "^10.12.0 || >=12.0.0" 951 | } 952 | }, 953 | "node_modules/flatted": { 954 | "version": "3.3.1", 955 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", 956 | "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", 957 | "dev": true 958 | }, 959 | "node_modules/fs.realpath": { 960 | "version": "1.0.0", 961 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 962 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 963 | "dev": true 964 | }, 965 | "node_modules/glob": { 966 | "version": "7.2.3", 967 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 968 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 969 | "dev": true, 970 | "dependencies": { 971 | "fs.realpath": "^1.0.0", 972 | "inflight": "^1.0.4", 973 | "inherits": "2", 974 | "minimatch": "^3.1.1", 975 | "once": "^1.3.0", 976 | "path-is-absolute": "^1.0.0" 977 | }, 978 | "engines": { 979 | "node": "*" 980 | }, 981 | "funding": { 982 | "url": "https://github.com/sponsors/isaacs" 983 | } 984 | }, 985 | "node_modules/glob-parent": { 986 | "version": "6.0.2", 987 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 988 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 989 | "dev": true, 990 | "dependencies": { 991 | "is-glob": "^4.0.3" 992 | }, 993 | "engines": { 994 | "node": ">=10.13.0" 995 | } 996 | }, 997 | "node_modules/globals": { 998 | "version": "13.24.0", 999 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", 1000 | "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", 1001 | "dev": true, 1002 | "dependencies": { 1003 | "type-fest": "^0.20.2" 1004 | }, 1005 | "engines": { 1006 | "node": ">=8" 1007 | }, 1008 | "funding": { 1009 | "url": "https://github.com/sponsors/sindresorhus" 1010 | } 1011 | }, 1012 | "node_modules/globby": { 1013 | "version": "11.1.0", 1014 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", 1015 | "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", 1016 | "dev": true, 1017 | "dependencies": { 1018 | "array-union": "^2.1.0", 1019 | "dir-glob": "^3.0.1", 1020 | "fast-glob": "^3.2.9", 1021 | "ignore": "^5.2.0", 1022 | "merge2": "^1.4.1", 1023 | "slash": "^3.0.0" 1024 | }, 1025 | "engines": { 1026 | "node": ">=10" 1027 | }, 1028 | "funding": { 1029 | "url": "https://github.com/sponsors/sindresorhus" 1030 | } 1031 | }, 1032 | "node_modules/graphemer": { 1033 | "version": "1.4.0", 1034 | "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 1035 | "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 1036 | "dev": true 1037 | }, 1038 | "node_modules/has-flag": { 1039 | "version": "4.0.0", 1040 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1041 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1042 | "dev": true, 1043 | "engines": { 1044 | "node": ">=8" 1045 | } 1046 | }, 1047 | "node_modules/ignore": { 1048 | "version": "5.3.1", 1049 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", 1050 | "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", 1051 | "dev": true, 1052 | "engines": { 1053 | "node": ">= 4" 1054 | } 1055 | }, 1056 | "node_modules/import-fresh": { 1057 | "version": "3.3.0", 1058 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 1059 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 1060 | "dev": true, 1061 | "dependencies": { 1062 | "parent-module": "^1.0.0", 1063 | "resolve-from": "^4.0.0" 1064 | }, 1065 | "engines": { 1066 | "node": ">=6" 1067 | }, 1068 | "funding": { 1069 | "url": "https://github.com/sponsors/sindresorhus" 1070 | } 1071 | }, 1072 | "node_modules/imurmurhash": { 1073 | "version": "0.1.4", 1074 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1075 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 1076 | "dev": true, 1077 | "engines": { 1078 | "node": ">=0.8.19" 1079 | } 1080 | }, 1081 | "node_modules/inflight": { 1082 | "version": "1.0.6", 1083 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1084 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1085 | "dev": true, 1086 | "dependencies": { 1087 | "once": "^1.3.0", 1088 | "wrappy": "1" 1089 | } 1090 | }, 1091 | "node_modules/inherits": { 1092 | "version": "2.0.4", 1093 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1094 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1095 | "dev": true 1096 | }, 1097 | "node_modules/is-extglob": { 1098 | "version": "2.1.1", 1099 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1100 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1101 | "dev": true, 1102 | "engines": { 1103 | "node": ">=0.10.0" 1104 | } 1105 | }, 1106 | "node_modules/is-glob": { 1107 | "version": "4.0.3", 1108 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1109 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1110 | "dev": true, 1111 | "dependencies": { 1112 | "is-extglob": "^2.1.1" 1113 | }, 1114 | "engines": { 1115 | "node": ">=0.10.0" 1116 | } 1117 | }, 1118 | "node_modules/is-number": { 1119 | "version": "7.0.0", 1120 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1121 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1122 | "dev": true, 1123 | "engines": { 1124 | "node": ">=0.12.0" 1125 | } 1126 | }, 1127 | "node_modules/is-path-inside": { 1128 | "version": "3.0.3", 1129 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 1130 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 1131 | "dev": true, 1132 | "engines": { 1133 | "node": ">=8" 1134 | } 1135 | }, 1136 | "node_modules/isexe": { 1137 | "version": "2.0.0", 1138 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1139 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1140 | "dev": true 1141 | }, 1142 | "node_modules/js-yaml": { 1143 | "version": "4.1.0", 1144 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1145 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1146 | "dev": true, 1147 | "dependencies": { 1148 | "argparse": "^2.0.1" 1149 | }, 1150 | "bin": { 1151 | "js-yaml": "bin/js-yaml.js" 1152 | } 1153 | }, 1154 | "node_modules/json-buffer": { 1155 | "version": "3.0.1", 1156 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 1157 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 1158 | "dev": true 1159 | }, 1160 | "node_modules/json-schema-traverse": { 1161 | "version": "1.0.0", 1162 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 1163 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" 1164 | }, 1165 | "node_modules/json-stable-stringify-without-jsonify": { 1166 | "version": "1.0.1", 1167 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1168 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 1169 | "dev": true 1170 | }, 1171 | "node_modules/keyv": { 1172 | "version": "4.5.4", 1173 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 1174 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 1175 | "dev": true, 1176 | "dependencies": { 1177 | "json-buffer": "3.0.1" 1178 | } 1179 | }, 1180 | "node_modules/levn": { 1181 | "version": "0.4.1", 1182 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1183 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1184 | "dev": true, 1185 | "dependencies": { 1186 | "prelude-ls": "^1.2.1", 1187 | "type-check": "~0.4.0" 1188 | }, 1189 | "engines": { 1190 | "node": ">= 0.8.0" 1191 | } 1192 | }, 1193 | "node_modules/locate-path": { 1194 | "version": "6.0.0", 1195 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1196 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1197 | "dev": true, 1198 | "dependencies": { 1199 | "p-locate": "^5.0.0" 1200 | }, 1201 | "engines": { 1202 | "node": ">=10" 1203 | }, 1204 | "funding": { 1205 | "url": "https://github.com/sponsors/sindresorhus" 1206 | } 1207 | }, 1208 | "node_modules/lodash.merge": { 1209 | "version": "4.6.2", 1210 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 1211 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 1212 | "dev": true 1213 | }, 1214 | "node_modules/lru-cache": { 1215 | "version": "6.0.0", 1216 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1217 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1218 | "dev": true, 1219 | "dependencies": { 1220 | "yallist": "^4.0.0" 1221 | }, 1222 | "engines": { 1223 | "node": ">=10" 1224 | } 1225 | }, 1226 | "node_modules/merge2": { 1227 | "version": "1.4.1", 1228 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1229 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1230 | "dev": true, 1231 | "engines": { 1232 | "node": ">= 8" 1233 | } 1234 | }, 1235 | "node_modules/micromatch": { 1236 | "version": "4.0.5", 1237 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 1238 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 1239 | "dev": true, 1240 | "dependencies": { 1241 | "braces": "^3.0.2", 1242 | "picomatch": "^2.3.1" 1243 | }, 1244 | "engines": { 1245 | "node": ">=8.6" 1246 | } 1247 | }, 1248 | "node_modules/minimatch": { 1249 | "version": "3.1.2", 1250 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1251 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1252 | "dev": true, 1253 | "dependencies": { 1254 | "brace-expansion": "^1.1.7" 1255 | }, 1256 | "engines": { 1257 | "node": "*" 1258 | } 1259 | }, 1260 | "node_modules/ms": { 1261 | "version": "2.1.2", 1262 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1263 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1264 | "dev": true 1265 | }, 1266 | "node_modules/natural-compare": { 1267 | "version": "1.4.0", 1268 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1269 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 1270 | "dev": true 1271 | }, 1272 | "node_modules/once": { 1273 | "version": "1.4.0", 1274 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1275 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1276 | "dev": true, 1277 | "dependencies": { 1278 | "wrappy": "1" 1279 | } 1280 | }, 1281 | "node_modules/optionator": { 1282 | "version": "0.9.3", 1283 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", 1284 | "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", 1285 | "dev": true, 1286 | "dependencies": { 1287 | "@aashutoshrathi/word-wrap": "^1.2.3", 1288 | "deep-is": "^0.1.3", 1289 | "fast-levenshtein": "^2.0.6", 1290 | "levn": "^0.4.1", 1291 | "prelude-ls": "^1.2.1", 1292 | "type-check": "^0.4.0" 1293 | }, 1294 | "engines": { 1295 | "node": ">= 0.8.0" 1296 | } 1297 | }, 1298 | "node_modules/p-limit": { 1299 | "version": "3.1.0", 1300 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1301 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1302 | "dev": true, 1303 | "dependencies": { 1304 | "yocto-queue": "^0.1.0" 1305 | }, 1306 | "engines": { 1307 | "node": ">=10" 1308 | }, 1309 | "funding": { 1310 | "url": "https://github.com/sponsors/sindresorhus" 1311 | } 1312 | }, 1313 | "node_modules/p-locate": { 1314 | "version": "5.0.0", 1315 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1316 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1317 | "dev": true, 1318 | "dependencies": { 1319 | "p-limit": "^3.0.2" 1320 | }, 1321 | "engines": { 1322 | "node": ">=10" 1323 | }, 1324 | "funding": { 1325 | "url": "https://github.com/sponsors/sindresorhus" 1326 | } 1327 | }, 1328 | "node_modules/parent-module": { 1329 | "version": "1.0.1", 1330 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1331 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1332 | "dev": true, 1333 | "dependencies": { 1334 | "callsites": "^3.0.0" 1335 | }, 1336 | "engines": { 1337 | "node": ">=6" 1338 | } 1339 | }, 1340 | "node_modules/path-exists": { 1341 | "version": "4.0.0", 1342 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1343 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1344 | "dev": true, 1345 | "engines": { 1346 | "node": ">=8" 1347 | } 1348 | }, 1349 | "node_modules/path-is-absolute": { 1350 | "version": "1.0.1", 1351 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1352 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1353 | "dev": true, 1354 | "engines": { 1355 | "node": ">=0.10.0" 1356 | } 1357 | }, 1358 | "node_modules/path-key": { 1359 | "version": "3.1.1", 1360 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1361 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1362 | "dev": true, 1363 | "engines": { 1364 | "node": ">=8" 1365 | } 1366 | }, 1367 | "node_modules/path-type": { 1368 | "version": "4.0.0", 1369 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 1370 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 1371 | "dev": true, 1372 | "engines": { 1373 | "node": ">=8" 1374 | } 1375 | }, 1376 | "node_modules/picomatch": { 1377 | "version": "2.3.1", 1378 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1379 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1380 | "dev": true, 1381 | "engines": { 1382 | "node": ">=8.6" 1383 | }, 1384 | "funding": { 1385 | "url": "https://github.com/sponsors/jonschlinkert" 1386 | } 1387 | }, 1388 | "node_modules/prelude-ls": { 1389 | "version": "1.2.1", 1390 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1391 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1392 | "dev": true, 1393 | "engines": { 1394 | "node": ">= 0.8.0" 1395 | } 1396 | }, 1397 | "node_modules/punycode": { 1398 | "version": "2.3.1", 1399 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1400 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1401 | "engines": { 1402 | "node": ">=6" 1403 | } 1404 | }, 1405 | "node_modules/queue-microtask": { 1406 | "version": "1.2.3", 1407 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1408 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1409 | "dev": true, 1410 | "funding": [ 1411 | { 1412 | "type": "github", 1413 | "url": "https://github.com/sponsors/feross" 1414 | }, 1415 | { 1416 | "type": "patreon", 1417 | "url": "https://www.patreon.com/feross" 1418 | }, 1419 | { 1420 | "type": "consulting", 1421 | "url": "https://feross.org/support" 1422 | } 1423 | ] 1424 | }, 1425 | "node_modules/require-from-string": { 1426 | "version": "2.0.2", 1427 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1428 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 1429 | "engines": { 1430 | "node": ">=0.10.0" 1431 | } 1432 | }, 1433 | "node_modules/resolve-from": { 1434 | "version": "4.0.0", 1435 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1436 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1437 | "dev": true, 1438 | "engines": { 1439 | "node": ">=4" 1440 | } 1441 | }, 1442 | "node_modules/reusify": { 1443 | "version": "1.0.4", 1444 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1445 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1446 | "dev": true, 1447 | "engines": { 1448 | "iojs": ">=1.0.0", 1449 | "node": ">=0.10.0" 1450 | } 1451 | }, 1452 | "node_modules/rimraf": { 1453 | "version": "3.0.2", 1454 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1455 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1456 | "dev": true, 1457 | "dependencies": { 1458 | "glob": "^7.1.3" 1459 | }, 1460 | "bin": { 1461 | "rimraf": "bin.js" 1462 | }, 1463 | "funding": { 1464 | "url": "https://github.com/sponsors/isaacs" 1465 | } 1466 | }, 1467 | "node_modules/run-parallel": { 1468 | "version": "1.2.0", 1469 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1470 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1471 | "dev": true, 1472 | "funding": [ 1473 | { 1474 | "type": "github", 1475 | "url": "https://github.com/sponsors/feross" 1476 | }, 1477 | { 1478 | "type": "patreon", 1479 | "url": "https://www.patreon.com/feross" 1480 | }, 1481 | { 1482 | "type": "consulting", 1483 | "url": "https://feross.org/support" 1484 | } 1485 | ], 1486 | "dependencies": { 1487 | "queue-microtask": "^1.2.2" 1488 | } 1489 | }, 1490 | "node_modules/semver": { 1491 | "version": "7.6.0", 1492 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", 1493 | "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", 1494 | "dev": true, 1495 | "dependencies": { 1496 | "lru-cache": "^6.0.0" 1497 | }, 1498 | "bin": { 1499 | "semver": "bin/semver.js" 1500 | }, 1501 | "engines": { 1502 | "node": ">=10" 1503 | } 1504 | }, 1505 | "node_modules/shebang-command": { 1506 | "version": "2.0.0", 1507 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1508 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1509 | "dev": true, 1510 | "dependencies": { 1511 | "shebang-regex": "^3.0.0" 1512 | }, 1513 | "engines": { 1514 | "node": ">=8" 1515 | } 1516 | }, 1517 | "node_modules/shebang-regex": { 1518 | "version": "3.0.0", 1519 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1520 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1521 | "dev": true, 1522 | "engines": { 1523 | "node": ">=8" 1524 | } 1525 | }, 1526 | "node_modules/slash": { 1527 | "version": "3.0.0", 1528 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 1529 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 1530 | "dev": true, 1531 | "engines": { 1532 | "node": ">=8" 1533 | } 1534 | }, 1535 | "node_modules/strip-ansi": { 1536 | "version": "6.0.1", 1537 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1538 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1539 | "dev": true, 1540 | "dependencies": { 1541 | "ansi-regex": "^5.0.1" 1542 | }, 1543 | "engines": { 1544 | "node": ">=8" 1545 | } 1546 | }, 1547 | "node_modules/strip-json-comments": { 1548 | "version": "3.1.1", 1549 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1550 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1551 | "dev": true, 1552 | "engines": { 1553 | "node": ">=8" 1554 | }, 1555 | "funding": { 1556 | "url": "https://github.com/sponsors/sindresorhus" 1557 | } 1558 | }, 1559 | "node_modules/supports-color": { 1560 | "version": "7.2.0", 1561 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1562 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1563 | "dev": true, 1564 | "dependencies": { 1565 | "has-flag": "^4.0.0" 1566 | }, 1567 | "engines": { 1568 | "node": ">=8" 1569 | } 1570 | }, 1571 | "node_modules/text-table": { 1572 | "version": "0.2.0", 1573 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1574 | "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 1575 | "dev": true 1576 | }, 1577 | "node_modules/to-regex-range": { 1578 | "version": "5.0.1", 1579 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1580 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1581 | "dev": true, 1582 | "dependencies": { 1583 | "is-number": "^7.0.0" 1584 | }, 1585 | "engines": { 1586 | "node": ">=8.0" 1587 | } 1588 | }, 1589 | "node_modules/ts-api-utils": { 1590 | "version": "1.3.0", 1591 | "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", 1592 | "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", 1593 | "dev": true, 1594 | "engines": { 1595 | "node": ">=16" 1596 | }, 1597 | "peerDependencies": { 1598 | "typescript": ">=4.2.0" 1599 | } 1600 | }, 1601 | "node_modules/type-check": { 1602 | "version": "0.4.0", 1603 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1604 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1605 | "dev": true, 1606 | "dependencies": { 1607 | "prelude-ls": "^1.2.1" 1608 | }, 1609 | "engines": { 1610 | "node": ">= 0.8.0" 1611 | } 1612 | }, 1613 | "node_modules/type-fest": { 1614 | "version": "0.20.2", 1615 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 1616 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 1617 | "dev": true, 1618 | "engines": { 1619 | "node": ">=10" 1620 | }, 1621 | "funding": { 1622 | "url": "https://github.com/sponsors/sindresorhus" 1623 | } 1624 | }, 1625 | "node_modules/typescript": { 1626 | "version": "5.4.2", 1627 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", 1628 | "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", 1629 | "dev": true, 1630 | "bin": { 1631 | "tsc": "bin/tsc", 1632 | "tsserver": "bin/tsserver" 1633 | }, 1634 | "engines": { 1635 | "node": ">=14.17" 1636 | } 1637 | }, 1638 | "node_modules/undici-types": { 1639 | "version": "5.26.5", 1640 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1641 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 1642 | "dev": true 1643 | }, 1644 | "node_modules/uri-js": { 1645 | "version": "4.4.1", 1646 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1647 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1648 | "dependencies": { 1649 | "punycode": "^2.1.0" 1650 | } 1651 | }, 1652 | "node_modules/which": { 1653 | "version": "2.0.2", 1654 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1655 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1656 | "dev": true, 1657 | "dependencies": { 1658 | "isexe": "^2.0.0" 1659 | }, 1660 | "bin": { 1661 | "node-which": "bin/node-which" 1662 | }, 1663 | "engines": { 1664 | "node": ">= 8" 1665 | } 1666 | }, 1667 | "node_modules/wrappy": { 1668 | "version": "1.0.2", 1669 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1670 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1671 | "dev": true 1672 | }, 1673 | "node_modules/yallist": { 1674 | "version": "4.0.0", 1675 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1676 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 1677 | "dev": true 1678 | }, 1679 | "node_modules/yocto-queue": { 1680 | "version": "0.1.0", 1681 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1682 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1683 | "dev": true, 1684 | "engines": { 1685 | "node": ">=10" 1686 | }, 1687 | "funding": { 1688 | "url": "https://github.com/sponsors/sindresorhus" 1689 | } 1690 | } 1691 | } 1692 | } 1693 | --------------------------------------------------------------------------------