@accountmade/questionnaire-parser
v0.1.1
Published
Parse a SIG / CAIQ / VSA / VSAQ security questionnaire (xlsx, docx, pdf, csv) into structured JSON. Ingest + normalize + framework detection only — no answers, no auto-fill.
Maintainers
Readme
questionnaire-parser
Parse a security questionnaire — SIG, CAIQ, VSA, VSAQ, or a bespoke one — from
.xlsx, .docx, .pdf, or .csv into structured JSON: a normalized list of
questions plus the detected framework(s), with evidence and confidence.
Ingest and normalize only. This library extracts and classifies questions. It does not answer them, auto-fill responses, rank answers, or ship an answer library — by design. It's the parsing primitive, nothing more.
npm install questionnaire-parserCLI
npx qparse ./SIG-Core-2025.xlsx --pretty
npx qparse ./caiq.xlsx --questions-only > questions.jsonLibrary
import { parseFile } from "questionnaire-parser";
const result = await parseFile("./caiq-v4.xlsx");
result.meta.questionCount; // 261
result.primaryFramework?.name; // "CAIQ v4 (CSA Cloud Controls Matrix)"
result.primaryFramework?.confidence; // "high"
result.questions[0]; // { id: "IAM-14", text: "Is MFA enforced…", section: "Identity & Access Management" }Parse an in-memory buffer (e.g. a browser upload passed to a server) instead:
import { parseBuffer } from "questionnaire-parser";
const result = await parseBuffer(buffer, "xlsx", "upload.xlsx");Or run the pure core directly on rows/text you've already extracted:
import { extractFromRows, detectFrameworks, pickPrimary } from "questionnaire-parser";
const questions = extractFromRows(rows, "Sheet1");
const frameworks = detectFrameworks(questions, rawText, sheetNames);
const primary = pickPrimary(frameworks); // null when bespoke/ambiguousOutput shape
{
"questions": [
{ "id": "IAM-14", "text": "Is MFA enforced for privileged access?", "section": "Identity & Access Management" }
],
"frameworks": [
{
"framework": "caiq",
"name": "CAIQ v4 (CSA Cloud Controls Matrix)",
"confidence": "high",
"evidence": ["43 control IDs match the CAIQ v4 grammar", "title/header text: \"caiq\""],
"matchedIdCount": 43,
"source": "CSA Cloud Controls Matrix / CAIQ v4 (cloudsecurityalliance.org)",
"score": 133
}
],
"primaryFramework": { "framework": "caiq", "...": "..." },
"meta": { "file": "caiq-v4.xlsx", "format": "xlsx", "questionCount": 261 }
}How detection works
One generic scorer runs over a table of framework fingerprints (control-ID grammar, header/tab strings, approximate size). There is no per-framework code branch — adding a framework is a data entry, not a new code path. ID grammar and header text dominate; question count is only a weak tiebreaker. SOC 2 and ISO 27001 are treated as cross-maps (they often appear inside another questionnaire) and are down-weighted for primary selection.
Supported fingerprints: CAIQ (CSA CCM), SIG (Shared Assessments), SOC 2, ISO 27001, Google VSAQ, Vendor Security Alliance (VSA).
Development
npm install
npm test # runs the core tests (node:test via tsx)
npm run build # emits dist/ (ESM + types)License
MIT. Framework names and control-ID grammars are referenced for interoperability and belong to their respective publishers (CSA, Shared Assessments, AICPA, ISO, NIST, Google).
