From e416b74df18bd9d97c5d06cfd2f55b1c714a101d Mon Sep 17 00:00:00 2001 From: PipeItToDevNull Date: Sun, 9 Nov 2025 13:51:49 -0800 Subject: [PATCH 01/20] add variables for limits --- README.md | 8 ++++++++ api/api.js | 34 ++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c8cf517..7e28931 100755 --- a/README.md +++ b/README.md @@ -40,6 +40,14 @@ The following Secrets and Variables must be configured under the "Actions" conte - REACT_APP_SITE_NAME ## API +### Variables + +``` +ENABLE_CORS +RATE_LIMIT_S +FILE_SIZE_MB +``` + ### Local development On a Windows host with Docker installed and using Windows containers execute the following from inside the `api` directory to build and launch the [WinDebug-Container](https://github.com/PipeItToDevNull/WinDebug-Container) based API. diff --git a/api/api.js b/api/api.js index c03f4db..24cf7e5 100755 --- a/api/api.js +++ b/api/api.js @@ -48,6 +48,28 @@ if (!fs.existsSync(resultsDir)) { fs.mkdirSync(resultsDir); } +// Read file size, rate limit window, and max hits from environment variables +const FILE_SIZE_MB = Number.isNaN(parseInt(process.env.FILE_SIZE_MB, 10)) + ? 10 + : parseInt(process.env.FILE_SIZE_MB, 10); // Default is 10 MB + +const RATE_LIMIT_S = Number.isNaN(parseInt(process.env.RATE_LIMIT_S, 10)) + ? 180 + : parseInt(process.env.RATE_LIMIT_S, 10); // Default 180 seconds (3 minutes) + +const RATE_LIMIT_MAX = Number.isNaN(parseInt(process.env.RATE_LIMIT_MAX, 10)) + ? 10 + : parseInt(process.env.RATE_LIMIT_MAX, 10); // Default 10 requests per window + +// Convert to bytes and milliseconds +const FILE_SIZE_BYTES = FILE_SIZE_MB * 1024 * 1024; +const RATE_LIMIT_MS = RATE_LIMIT_S * 1000; + +// Log the configured limits +logger.info(`File size limit: ${FILE_SIZE_MB}MB (${FILE_SIZE_BYTES} bytes)`); +logger.info(`Rate limit window: ${RATE_LIMIT_S} seconds (${RATE_LIMIT_MS} ms), max ${RATE_LIMIT_MAX} requests per window`); + + // Configure multer for file uploads const storage = multer.diskStorage({ destination: (req, file, cb) => { @@ -72,10 +94,10 @@ if (process.env.ENABLE_CORS === 'true') { }); } -// Size limit of 10M +// Size limit from environment variable (default 10MB) const upload = multer({ storage: storage, - limits: { fileSize: 10 * 1024 * 1024 } + limits: { fileSize: FILE_SIZE_BYTES } }); // Add security headers to all responses @@ -83,8 +105,8 @@ app.use(helmet()); // Add security headers to all responses // Rate limiting middleware to prevent abuse const limiter = rateLimit({ - windowMs: 3 * 60 * 1000, // 3 minutes - max: 10 // limit each IP to 10 requests per windowMs + windowMs: RATE_LIMIT_MS, // from environment variable + max: RATE_LIMIT_MAX // limit each IP to X requests per windowMs }); app.use(limiter); @@ -374,8 +396,8 @@ app.use(async (req, res, next) => { app.use((err, req, res, next) => { if (err instanceof multer.MulterError) { if (err.code === 'LIMIT_FILE_SIZE') { - logger.warn('File size exceeds the limit of 10MB'); - return res.status(400).send('File size exceeds the limit of 10MB'); + logger.warn(`File size exceeds the limit of ${FILE_SIZE_MB}MB`); + return res.status(400).send(`File size exceeds the limit of ${FILE_SIZE_MB}MB`); } } From 130666bd079f490a9973a244c5403a9c4d70d571 Mon Sep 17 00:00:00 2001 From: PipeItToDevNull Date: Sun, 9 Nov 2025 13:54:39 -0800 Subject: [PATCH 02/20] add another var --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7e28931..49e2765 100755 --- a/README.md +++ b/README.md @@ -43,9 +43,10 @@ The following Secrets and Variables must be configured under the "Actions" conte ### Variables ``` -ENABLE_CORS -RATE_LIMIT_S -FILE_SIZE_MB +ENABLE_CORS \\ Default is false, set to true when testing otherwise you will get CORS failures. In prod this should be handled by your proxy +RATE_LIMIT_S \\ The duration of your rate limit expressed in seconds +RATE_LIMIT_MAX \\ How many requests a client can make in RATE_LIMIT_S before being blocked +FILE_SIZE_MB \\ How large of a file can be processed. This same size should be configured on your proxy for a more reliable failure. ``` ### Local development From 7883c54e543bce976c2ce09f50174ddb05f06018 Mon Sep 17 00:00:00 2001 From: PipeItToDevNull Date: Sun, 9 Nov 2025 14:21:48 -0800 Subject: [PATCH 03/20] split out post-processing into their own files --- api/post-process.js | 37 ++++++++++++++++++++++++++----------- api/post-processors/133.js | 4 ++++ api/post-processors/9f.js | 5 +++++ 3 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 api/post-processors/133.js create mode 100644 api/post-processors/9f.js diff --git a/api/post-process.js b/api/post-process.js index ecd0ffb..6403fdd 100644 --- a/api/post-process.js +++ b/api/post-process.js @@ -1,5 +1,8 @@ import { exec } from 'child_process'; import winston from 'winston'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath, pathToFileURL } from 'url'; // Configure Winston logger const logger = winston.createLogger({ @@ -14,15 +17,26 @@ const logger = winston.createLogger({ ] }); -// Configuration object for bugcheck commands -const bugcheckCommands = { - '9f': (parser, dmp, args) => `${parser} -z ${dmp} -c "k; !devstack ${args[1]} ; q"`, - '133': (parser, dmp) => `${parser} -z ${dmp} -c "k; !dpcwatchdog ; q"`, - // Add more bugcheck commands here as needed - // '': (dmp, args) => `${parser} -z ${dmp} -c "k; ; q"`, - // Args can be used in a command ${args[#]} - // Arg counts start at 0 so "Arg1" is ${args[0]} -}; +const bugcheckCommands = {}; +const processorsDir = path.join(path.dirname(fileURLToPath(import.meta.url)), 'post-processors'); + +try { + const files = fs.readdirSync(processorsDir).filter(f => f.endsWith('.js')); + for (const file of files) { + const name = path.basename(file, '.js'); + const filePath = path.join(processorsDir, file); + try { + // eslint-disable-next-line no-await-in-loop + const mod = await import(pathToFileURL(filePath).href); + bugcheckCommands[name] = mod.default; + logger.info(`Loaded post-processor: ${name} -> ${filePath}`); + } catch (err) { + logger.error(`Failed to load post-processor ${filePath}: ${err.stack || err}`); + } + } +} catch (err) { + logger.error(`Unable to read post-processors directory "${processorsDir}": ${err.stack || err}`); +} // Function to execute a command and return a promise const executeCommand = (command) => { @@ -42,8 +56,9 @@ const executeCommand = (command) => { // Function to perform additional operations on the analysis results const postProcessResults = async (results, parser) => { for (const result of results) { - const commandGenerator = bugcheckCommands[result.bugcheck]; + const commandGenerator = bugcheckCommands[String(result.bugcheck || '').toLowerCase()]; if (commandGenerator) { + // commandGenerator expected signature: (parser, dmp, args) => string const command = commandGenerator(parser, result.dmp, result.args); logger.info(`Executing command: ${command}`); try { @@ -55,7 +70,7 @@ const postProcessResults = async (results, parser) => { logger.error(`An error occured while post-processing the file: ${error}`); } } else { - result.post = "No post processing configured for this bugcheck" // Add a null post key if no command is run + result.post = "No post processing configured for this bugcheck"; logger.info(`No command for bugcheck: ${result.bugcheck}`); } } diff --git a/api/post-processors/133.js b/api/post-processors/133.js new file mode 100644 index 0000000..f025ae5 --- /dev/null +++ b/api/post-processors/133.js @@ -0,0 +1,4 @@ +// Generate command for bugcheck 133 (DPC_WATCHDOG_VIOLATION) +export default (parser, dmp) => { + return `${parser} -z ${dmp} -c "k; !dpcwatchdog ; q"`; +}; \ No newline at end of file diff --git a/api/post-processors/9f.js b/api/post-processors/9f.js new file mode 100644 index 0000000..e066f61 --- /dev/null +++ b/api/post-processors/9f.js @@ -0,0 +1,5 @@ +// Generate command for bugcheck 9f (PAGE_FAULT_IN_NONPAGED_AREA / example) +export default (parser, dmp, args = []) => { + const devstackArg = args[1] ? args[1] : ''; + return `${parser} -z ${dmp} -c "k; !devstack ${devstackArg} ; q"`; +}; \ No newline at end of file From d5f936055363cd1dbd307b6752a4a4473e1ef802 Mon Sep 17 00:00:00 2001 From: PipeItToDevNull Date: Sun, 9 Nov 2025 14:21:52 -0800 Subject: [PATCH 04/20] updates --- swa/package-lock.json | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/swa/package-lock.json b/swa/package-lock.json index 5525942..134b3ba 100644 --- a/swa/package-lock.json +++ b/swa/package-lock.json @@ -65,6 +65,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -714,6 +715,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz", "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1597,6 +1599,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", @@ -3528,8 +3531,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -3902,6 +3904,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", "@typescript-eslint/scope-manager": "5.62.0", @@ -3955,6 +3958,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -4324,6 +4328,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4422,6 +4427,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5329,6 +5335,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -6696,8 +6703,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -7184,6 +7190,7 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -9969,6 +9976,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -10854,6 +10862,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -11247,7 +11256,6 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -12238,6 +12246,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -13372,6 +13381,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13719,6 +13729,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -13850,6 +13861,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -13904,6 +13916,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -14397,6 +14410,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -14639,6 +14653,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -15978,20 +15993,6 @@ } } }, - "node_modules/tailwindcss/node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -16332,6 +16333,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -16761,6 +16763,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -16832,6 +16835,7 @@ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -17244,6 +17248,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", From fd9cfcff07d6193df074ffbb9064d3ca6ed27f30 Mon Sep 17 00:00:00 2001 From: PipeItToDevNull Date: Sun, 9 Nov 2025 14:45:22 -0800 Subject: [PATCH 05/20] add summary at top of page --- swa/src/ResultPage.js | 53 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/swa/src/ResultPage.js b/swa/src/ResultPage.js index 57f044a..d988957 100644 --- a/swa/src/ResultPage.js +++ b/swa/src/ResultPage.js @@ -64,6 +64,26 @@ const renderJsonToHtml = (data) => { ); }; +// New helper: recursively collect "bugchecks" or "bugcheck" values (arrays or strings) +const collectBugchecks = (node, collector = []) => { + if (Array.isArray(node)) { + node.forEach(item => collectBugchecks(item, collector)); + return collector; + } + if (node && typeof node === 'object') { + // support both "bugchecks" (plural) and "bugcheck" (singular) + ['bugchecks', 'bugcheck'].forEach((key) => { + if (Object.prototype.hasOwnProperty.call(node, key)) { + const v = node[key]; + if (Array.isArray(v)) collector.push(...v.map(String)); + else if (v != null) collector.push(String(v)); + } + }); + Object.values(node).forEach(v => collectBugchecks(v, collector)); + } + return collector; +}; + const ResultPage = () => { const { uuid } = useParams(); const [loading, setLoading] = useState(true); @@ -106,7 +126,38 @@ const ResultPage = () => { {error &&

{error}

} {responseData && ( isValidJson(responseData) - ? <>{renderJsonToHtml(JSON.parse(responseData))} + ? (() => { + const parsed = JSON.parse(responseData); + // If we have multiple results (array with length > 1), build a summary block + if (Array.isArray(parsed) && parsed.length > 1) { + const all = collectBugchecks(parsed); + const counts = all.reduce((acc, name) => { + acc[name] = (acc[name] || 0) + 1; + return acc; + }, {}); + return ( + <> +
+

Summary

+ {Object.keys(counts).length === 0 ? ( +

No bugchecks found.

+ ) : ( +
    + {Object.entries(counts).map(([name, cnt]) => ( +
  • {name} — {cnt}
  • + ))} +
+ )} +
+
+ {renderJsonToHtml(parsed)} +
+ + ); + } + // single result or not an array -> normal render + return <>{renderJsonToHtml(parsed)}; + })() :

Error: Invalid JSON received from backend.

)} From 3de4e8658fe59f929a3aa5beba339a91b1e24598 Mon Sep 17 00:00:00 2001 From: PipeItToDevNull Date: Sun, 9 Nov 2025 15:01:20 -0800 Subject: [PATCH 06/20] add summary --- swa/src/ResultPage.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/swa/src/ResultPage.js b/swa/src/ResultPage.js index d988957..c36a765 100644 --- a/swa/src/ResultPage.js +++ b/swa/src/ResultPage.js @@ -137,7 +137,7 @@ const ResultPage = () => { }, {}); return ( <> -
+

Summary

{Object.keys(counts).length === 0 ? (

No bugchecks found.

@@ -149,9 +149,7 @@ const ResultPage = () => { )}
-
{renderJsonToHtml(parsed)} -
); } From 7c1ed32c184579b1bd94d975d5111b89e775e200 Mon Sep 17 00:00:00 2001 From: PipeItToDevNull Date: Sun, 9 Nov 2025 15:17:55 -0800 Subject: [PATCH 07/20] capture the human readable bugcheck used in summaries --- api/analyze.js | 25 +++++++++++++++++++------ swa/src/ResultPage.js | 17 ++++++++--------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/api/analyze.js b/api/analyze.js index f560b05..6fba69d 100644 --- a/api/analyze.js +++ b/api/analyze.js @@ -60,13 +60,25 @@ const processResult = (dmp, rawContent) => { const analysisLines = infos[1].split('\n').filter(line => !line.includes('*') && !line.includes("Debugging Details:")); const analysis = analysisLines.join('\n').trim(); - // Extracting bugcheck and arguments - const bugcheckMatch = analysis.match(/\(([^)]+)\)/); - const bugcheck = bugcheckMatch ? bugcheckMatch[1] : null; + // Extract human name and code, e.g. "MEMORY_MANAGEMENT (1a)" + // bugcheckHuman => "MEMORY_MANAGEMENT", bugcheck => "1a" + let bugcheckHuman = null; + let bugcheck = null; + const bcMatch = analysis.match(/^\s*([A-Za-z0-9 _-]+?)\s*\(\s*([0-9A-Fa-fx]+)\s*\)/m); + if (bcMatch) { + bugcheckHuman = bcMatch[1].trim(); + bugcheck = bcMatch[2].trim(); + } else { + // fallback: capture any code-like value in parentheses + const bugcheckOnlyMatch = analysis.match(/\(([0-9A-Fa-fx]+)\)/); + if (bugcheckOnlyMatch) bugcheck = bugcheckOnlyMatch[1].trim(); + } + + // Arguments like: Arg1: 00000000... + const argMatches = analysis.match(/Arg\d:\s*([0-9a-fA-Fx]+)/g); + const args = argMatches ? argMatches.map(arg => arg.split(':')[1].trim()) : []; - const argMatches = analysis.match(/Arg\d: ([0-9a-fA-Fx]+)/g); - const args = argMatches ? argMatches.map(arg => arg.split(': ')[1]) : []; - logger.info(`Bugcheck: ${bugcheck}, Args: ${args}`); + logger.info(`Bugcheck: ${bugcheck} (${bugcheckHuman || 'unknown'}), Args: ${args}`); // Output object creation const output = { @@ -74,6 +86,7 @@ const processResult = (dmp, rawContent) => { dmpInfo: dmpInfo, analysis: analysis, bugcheck: bugcheck, + bugcheckHuman: bugcheckHuman, args: args, rawContent: rawContent }; diff --git a/swa/src/ResultPage.js b/swa/src/ResultPage.js index c36a765..7a71439 100644 --- a/swa/src/ResultPage.js +++ b/swa/src/ResultPage.js @@ -64,21 +64,20 @@ const renderJsonToHtml = (data) => { ); }; -// New helper: recursively collect "bugchecks" or "bugcheck" values (arrays or strings) +// Collect human readable bugcheck names from the result object to display a summary const collectBugchecks = (node, collector = []) => { if (Array.isArray(node)) { node.forEach(item => collectBugchecks(item, collector)); return collector; } if (node && typeof node === 'object') { - // support both "bugchecks" (plural) and "bugcheck" (singular) - ['bugchecks', 'bugcheck'].forEach((key) => { - if (Object.prototype.hasOwnProperty.call(node, key)) { - const v = node[key]; - if (Array.isArray(v)) collector.push(...v.map(String)); - else if (v != null) collector.push(String(v)); + if (Object.prototype.hasOwnProperty.call(node, 'bugcheckHuman')) { + const human = node.bugcheckHuman; + if (human != null) { + const s = String(human).trim(); + if (s) collector.push(s); } - }); + } Object.values(node).forEach(v => collectBugchecks(v, collector)); } return collector; @@ -144,7 +143,7 @@ const ResultPage = () => { ) : (
    {Object.entries(counts).map(([name, cnt]) => ( -
  • {name} — {cnt}
  • +
  • {name} x {cnt}
  • ))}
)} From f992ebb05610e04631776f710d8eebfae8211907 Mon Sep 17 00:00:00 2001 From: PipeItToDevNull Date: Sun, 9 Nov 2025 18:21:29 -0800 Subject: [PATCH 08/20] remove unused json renderer --- swa/src/App.js | 68 +------------------------------------------------- 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/swa/src/App.js b/swa/src/App.js index 040a4fe..2a04b81 100644 --- a/swa/src/App.js +++ b/swa/src/App.js @@ -119,72 +119,6 @@ const FileUpload = () => { }, {}); }; - // Function to render JSON object into HTML objects - const renderJsonToHtml = (data) => { - console.log("Data received:", data); // Debugging line - - // No clue what this does, but everything is rendered inside it - if (Array.isArray(data)) { - return data.map((item) => { - // Use a stable key: stringified item or item.key if available - const key = (item && typeof item === 'object' && item.key) ? item.key : JSON.stringify(item); - return ( -
- {renderJsonToHtml(item)} -
- ); - }); - } - - // Define the key order to display in - const order = [ - "dmpName", - "dmpInfo", - "analysis", - "post", - "rawContent", - ]; - const specialKeys = ["rawContent"]; - const sortedData = sortJson(data, order); - - // Convert object to array of key-value pairs - const keyValueArray = Object.entries(sortedData).map(([key, value]) => ({ key, value })); - - // Separate the special items - const specialItems = keyValueArray.filter(item => specialKeys.includes(item.key)); - const regularItems = keyValueArray.filter(item => !specialKeys.includes(item.key)); - - // Render the regular items - const regularRender = regularItems.map((item) => ( - -

- {item.key} -

-
- {item.value} -
-
- )); - - // Render the special items with their own method - const specialRender = specialItems.map((item) => ( -
-
- Raw results -
{item.value}
-
-
- )); - - // Combine both renders - return ( - <> - {regularRender} - {specialRender} - - ); - }; - return (
@@ -214,7 +148,7 @@ const FileUpload = () => { {!error && !responseData &&

{loading ? 'Processing...' : 'Upload your .dmp file or a .zip file containing multiple .dmp files directly or via a direct link.'}

} {error &&

{error}

} {responseData && ( - <>{renderJsonToHtml(JSON.parse(responseData))} +
{responseData}
)}