Skip to content

accountmade/questionnaire-parser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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-parser

CLI

npx qparse ./SIG-Core-2025.xlsx --pretty
npx qparse ./caiq.xlsx --questions-only > questions.json

Library

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/ambiguous

Output 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).

About

Parse SIG/CAIQ/VSA/VSAQ security questionnaires into structured JSON — ingest + framework detection only. No answers, no auto-fill.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors