@mitre/hdf-generators 3.7.1-rc.1 → 3.7.2-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/ruby-escape.ts","../src/control-stub.ts","../src/inspec-yml.ts","../src/profile-generator.ts","../src/merge.ts","../src/delta.ts","../src/delta-report.ts"],"sourcesContent":["/**\n * Escape a string for use as a Ruby string literal.\n *\n * Strategy:\n * - If the string contains both single and double quotes → %q() wrapper\n * - If the string contains only single quotes → double-quoted with escaping\n * - Otherwise → single-quoted with escaping\n *\n * Ported from ts-inspec-objects escapeQuotes(), cross-referenced with\n * inspec-parser's RubyRebuilder.\n */\nexport function escapeQuotes(s: string): string {\n const hasSingle = s.includes(\"'\");\n const hasDouble = s.includes('\"');\n\n if (hasSingle && hasDouble) {\n // %q() — escape backslashes before ) so Ruby doesn't treat \\) as escaped delimiter\n return `%q(${s.replace(/\\\\\\)/g, '\\\\\\\\)')})`;\n }\n\n if (hasSingle) {\n // Double-quoted: escape backslashes, then double quotes\n return `\"${s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n }\n\n // Single-quoted: escape backslashes, then single quotes\n return `'${s.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")}'`;\n}\n","import type { BaselineRequirement } from '@mitre/hdf-schema';\nimport { escapeQuotes } from './ruby-escape.js';\n\n/**\n * Detects when a code string already starts with `control 'ID' do`,\n * meaning it's a complete InSpec control file — not a body fragment.\n */\nconst FULL_CONTROL_BLOCK = /^\\s*control\\s+['\"]([^'\"]+)['\"]\\s+do\\b/;\n\n/**\n * Generate a Ruby InSpec control stub from an HDF BaselineRequirement.\n *\n * Output follows the InSpec DSL ordering convention:\n * control 'ID' do\n * title ...\n * desc ...\n * desc 'check', ...\n * impact ...\n * tag key: value\n * <code or stub comment>\n * end\n */\nexport function generateControlStub(req: BaselineRequirement): string {\n // If code is already a complete `control 'ID' do ... end` block (e.g.\n // from `-c controls/` reading whole .rb files), emit it as-is — wrapping\n // it again would produce nested control blocks, which InSpec rejects.\n // When the inner ID differs from req.id (e.g. an upgrade rename match\n // where current's code was carried into a renamed requirement), rewrite\n // the wrapper to match req.id so the file remains valid InSpec.\n if (req.code) {\n const m = FULL_CONTROL_BLOCK.exec(req.code);\n if (m && m[1] !== undefined) {\n const innerID = m[1];\n if (innerID === req.id) {\n return req.code;\n }\n const start = m.index + m[0].indexOf(innerID);\n return req.code.slice(0, start) + req.id + req.code.slice(start + innerID.length);\n }\n }\n\n const lines: string[] = [];\n\n lines.push(`control '${req.id}' do`);\n\n // Title\n if (req.title) {\n lines.push(` title ${escapeQuotes(req.title)}`);\n }\n\n // Descriptions: default first (as bare `desc`), then labeled\n const defaultDesc = req.descriptions.find((d) => d.label === 'default');\n if (defaultDesc) {\n lines.push(` desc ${escapeQuotes(defaultDesc.data)}`);\n }\n\n const seenDefault = new Set<string>();\n for (const d of req.descriptions) {\n if (d.label === 'default') {\n if (seenDefault.has('default')) {\n continue; // skip duplicate default\n }\n seenDefault.add('default');\n continue; // already emitted above as bare desc\n }\n lines.push(` desc '${d.label}', ${escapeQuotes(d.data)}`);\n }\n\n // Impact — always render with at least one decimal place for 0 and whole numbers\n if (req.impact !== undefined) {\n const impactStr = Number.isInteger(req.impact)\n ? req.impact.toFixed(1)\n : String(req.impact);\n lines.push(` impact ${impactStr}`);\n }\n\n // Tags\n for (const [key, value] of Object.entries(req.tags)) {\n lines.push(` ${formatTag(key, value)}`);\n }\n\n // Code body or stub placeholder\n if (req.code) {\n lines.push('');\n lines.push(req.code);\n } else {\n lines.push('');\n lines.push(' # TODO: Add InSpec test code here');\n }\n\n lines.push('end');\n lines.push(''); // trailing newline\n\n return lines.join('\\n');\n}\n\n/** Format a single tag key-value pair as Ruby DSL. */\nfunction formatTag(key: string, value: unknown): string {\n if (value === null || value === undefined) {\n return `tag ${key}: nil`;\n }\n\n if (Array.isArray(value)) {\n // Ruby array syntax: tag key: ['val1', 'val2']\n const items = value.map((v: unknown) => `'${String(v)}'`).join(', ');\n return `tag ${key}: [${items}]`;\n }\n\n if (typeof value === 'boolean') {\n return `tag ${key}: ${value}`;\n }\n\n if (typeof value === 'string') {\n return `tag ${key}: ${escapeQuotes(value)}`;\n }\n\n // Fallback for other types (numbers, objects)\n return `tag ${key}: ${JSON.stringify(value)}`;\n}\n","import type { HDFBaseline } from '@mitre/hdf-schema';\nimport type { GeneratorOptions } from './types.js';\n\n/**\n * Generate an inspec.yml YAML string from an HDF Baseline and options.\n *\n * Uses string interpolation — no YAML library needed since the structure\n * is fixed and shallow.\n */\nexport function generateInSpecYml(\n baseline: HDFBaseline,\n options?: GeneratorOptions,\n): string {\n const lines: string[] = [];\n const meta = options?.metadata;\n\n // Required: name\n lines.push(`name: ${baseline.name}`);\n\n // Optional metadata fields\n if (baseline.title) {\n lines.push(`title: ${baseline.title}`);\n }\n\n const maintainer = meta?.maintainer ?? baseline.maintainer;\n if (maintainer) {\n lines.push(`maintainer: ${maintainer}`);\n }\n\n const copyright = meta?.copyright ?? baseline.copyright;\n if (copyright) {\n lines.push(`copyright: ${copyright}`);\n }\n\n const license = meta?.license ?? baseline.license;\n if (license) {\n lines.push(`license: ${license}`);\n }\n\n if (baseline.summary) {\n lines.push(`summary: ${baseline.summary}`);\n }\n\n // Version: metadata override takes priority\n const version = meta?.version ?? baseline.version;\n if (version) {\n lines.push(`version: '${version}'`);\n }\n\n // InSpec version constraint\n const inspecVersion = options?.inspecVersion ?? '~>6.0';\n lines.push(`inspec_version: '${inspecVersion}'`);\n\n // Supports array\n if (baseline.supports && baseline.supports.length > 0) {\n lines.push('supports:');\n for (const support of baseline.supports) {\n const entries: string[] = [];\n if (support.platformName) {\n entries.push(` platform-name: ${support.platformName}`);\n }\n if (support.platformFamily) {\n entries.push(` platform-family: ${support.platformFamily}`);\n }\n if (support.platform) {\n entries.push(` platform: ${support.platform}`);\n }\n if (support.release) {\n entries.push(` release: ${support.release}`);\n }\n if (entries.length > 0) {\n lines.push(`- ${entries[0]!.trimStart()}`);\n for (let i = 1; i < entries.length; i++) {\n lines.push(entries[i]!);\n }\n }\n }\n }\n\n // Depends array\n if (baseline.depends && baseline.depends.length > 0) {\n lines.push('depends:');\n for (const dep of baseline.depends) {\n const entries: string[] = [];\n if (dep.name) entries.push(`name: ${dep.name}`);\n if (dep.git) entries.push(`git: ${dep.git}`);\n if (dep.url) entries.push(`url: ${dep.url}`);\n if (dep.path) entries.push(`path: ${dep.path}`);\n if (dep.branch) entries.push(`branch: ${dep.branch}`);\n if (dep.compliance) entries.push(`compliance: ${dep.compliance}`);\n if (dep.supermarket) entries.push(`supermarket: ${dep.supermarket}`);\n if (entries.length > 0) {\n lines.push(`- ${entries[0]}`);\n for (let i = 1; i < entries.length; i++) {\n lines.push(` ${entries[i]}`);\n }\n }\n }\n }\n\n // Inputs array\n if (baseline.inputs && baseline.inputs.length > 0) {\n lines.push('inputs:');\n for (const input of baseline.inputs) {\n for (const [key, value] of Object.entries(input)) {\n lines.push(`- ${key}: ${formatYamlValue(value)}`);\n }\n }\n }\n\n lines.push(''); // trailing newline\n return lines.join('\\n');\n}\n\n/** Format a value for inline YAML output. */\nfunction formatYamlValue(value: unknown): string {\n if (typeof value === 'boolean') return String(value);\n if (typeof value === 'number') return String(value);\n if (typeof value === 'string') return value;\n return JSON.stringify(value);\n}\n","import type { BaselineRequirement, HDFBaseline } from '@mitre/hdf-schema';\nimport type { GeneratorOptions, InSpecProfile } from './types.js';\nimport { generateControlStub } from './control-stub.js';\nimport { generateInSpecYml } from './inspec-yml.js';\n\n/**\n * Generate an in-memory InSpec profile from an HDF Baseline.\n *\n * Returns an InSpecProfile with inspec.yml content and a Map of\n * control filenames to Ruby source code. No file I/O — the CLI\n * is responsible for writing files to disk.\n */\nexport function generateInSpecProfile(\n baseline: HDFBaseline,\n options?: GeneratorOptions,\n): InSpecProfile {\n const inspecYml = generateInSpecYml(baseline, options);\n const controls = new Map<string, string>();\n\n if (baseline.requirements.length === 0) {\n return { inspecYml, controls };\n }\n\n if (options?.singleFile) {\n // All controls in a single file\n const stubs = baseline.requirements.map((req: BaselineRequirement) => generateControlStub(req));\n controls.set('controls/controls.rb', stubs.join('\\n'));\n } else {\n // One file per control — sanitize ID for safe filenames\n for (const req of baseline.requirements) {\n const safeId = req.id.replace(/\\.\\./g, '').replace(/[/\\\\]/g, '') || 'unknown';\n const filename = `controls/${safeId}.rb`;\n controls.set(filename, generateControlStub(req));\n }\n }\n\n return { inspecYml, controls };\n}\n","import type { BaselineRequirement, Description, Reference } from '@mitre/hdf-schema';\n\nexport type PreferSide = 'current' | 'upstream' | undefined;\n\n/**\n * Smart-merge a current and upstream requirement.\n *\n * Default (no prefer):\n * - ID: always upstream\n * - Scalars (title, impact, severity): upstream wins\n * - Tags: union, upstream wins key conflicts\n * - Descriptions: union by label, upstream wins on same label\n * - Code: current (preserve tests)\n * - Refs: union (deduplicated)\n *\n * prefer \"current\": scalars from current, current wins tag/desc conflicts\n * prefer \"upstream\": everything from upstream (full replacement)\n */\nexport function mergeRequirement(\n current: BaselineRequirement,\n upstream: BaselineRequirement,\n prefer?: PreferSide,\n): BaselineRequirement {\n const merged: BaselineRequirement = {\n // ID always comes from upstream\n id: upstream.id,\n\n // Scalars\n title: prefer === 'current' ? current.title : upstream.title,\n impact: prefer === 'current' ? current.impact : upstream.impact,\n severity: prefer === 'current' ? current.severity : upstream.severity,\n\n // Collections\n tags: mergeTags(current.tags, upstream.tags, prefer),\n descriptions: mergeDescriptions(current.descriptions, upstream.descriptions, prefer),\n refs: mergeRefs(current.refs, upstream.refs, prefer),\n\n // Code: current by default, upstream only with --prefer upstream\n code: prefer === 'upstream'\n ? upstream.code\n : (current.code ?? upstream.code),\n\n // SourceLocation follows scalars\n sourceLocation: prefer === 'current' ? current.sourceLocation : upstream.sourceLocation,\n };\n\n // Clean up undefined optional fields\n if (merged.code === undefined) delete merged.code;\n if (merged.severity === undefined) delete merged.severity;\n if (merged.sourceLocation === undefined) delete merged.sourceLocation;\n if (merged.refs === undefined || merged.refs.length === 0) delete merged.refs;\n\n return merged;\n}\n\n/**\n * Merge two tag maps.\n *\n * Default: union of keys; upstream wins on key conflicts.\n * prefer \"current\": union; current wins on key conflicts.\n * prefer \"upstream\": upstream replaces all.\n */\nexport function mergeTags(\n current: Record<string, any>, // eslint-disable-line @typescript-eslint/no-explicit-any\n upstream: Record<string, any>, // eslint-disable-line @typescript-eslint/no-explicit-any\n prefer?: PreferSide,\n): Record<string, any> { // eslint-disable-line @typescript-eslint/no-explicit-any\n if (prefer === 'upstream') {\n return { ...upstream };\n }\n\n const merged: Record<string, any> = { ...current }; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n for (const [key, value] of Object.entries(upstream)) {\n if (prefer === 'current') {\n if (!(key in merged)) {\n merged[key] = value;\n }\n } else {\n // Default: upstream wins on conflict\n merged[key] = value;\n }\n }\n\n return merged;\n}\n\n/**\n * Merge two description arrays by label.\n *\n * Default: union by label; upstream wins on label conflicts.\n * prefer \"current\": union; current wins on label conflicts.\n * prefer \"upstream\": upstream replaces all.\n */\nexport function mergeDescriptions(\n current: Description[],\n upstream: Description[],\n prefer?: PreferSide,\n): Description[] {\n if (prefer === 'upstream') {\n return [...upstream];\n }\n\n const byLabel = new Map<string, Description>();\n const order: string[] = [];\n\n // Start with current\n for (const d of current) {\n byLabel.set(d.label, d);\n order.push(d.label);\n }\n\n // Apply upstream\n for (const d of upstream) {\n if (byLabel.has(d.label)) {\n if (prefer !== 'current') {\n // Default: upstream wins\n byLabel.set(d.label, d);\n }\n } else {\n byLabel.set(d.label, d);\n order.push(d.label);\n }\n }\n\n return order.map(label => byLabel.get(label)!);\n}\n\n/**\n * Merge two reference arrays.\n *\n * Default: union, deduplicated by JSON key.\n * prefer \"current\": current only.\n * prefer \"upstream\": upstream only.\n */\nexport function mergeRefs(\n current?: Reference[],\n upstream?: Reference[],\n prefer?: PreferSide,\n): Reference[] | undefined {\n if (prefer === 'current') {\n return current ? [...current] : undefined;\n }\n if (prefer === 'upstream') {\n return upstream ? [...upstream] : undefined;\n }\n\n // Default: union, deduplicated\n if (!current && !upstream) return undefined;\n\n const seen = new Set<string>();\n const result: Reference[] = [];\n\n const addRef = (r: Reference): void => {\n const key = JSON.stringify(r);\n if (!seen.has(key)) {\n seen.add(key);\n result.push(r);\n }\n };\n\n if (current) current.forEach(addRef);\n if (upstream) upstream.forEach(addRef);\n\n return result.length > 0 ? result : undefined;\n}\n","import type { HDFBaseline, BaselineRequirement } from '@mitre/hdf-schema';\nimport type { UpgradeResult, UpgradeOptions, DeltaResult, DeltaOptions, LinkRecord, DeltaStatistics } from './delta-types.js';\nimport type { InSpecProfile } from './types.js';\nimport { mergeRequirement } from './merge.js';\nimport { generateControlStub } from './control-stub.js';\nimport { generateInSpecYml } from './inspec-yml.js';\n\n/**\n * Generate an upgraded HDF Baseline by smart-merging current and upstream requirements.\n *\n * For each upstream requirement:\n * - If matched: smart-merge current + upstream fields per mergeRequirement semantics\n * - If unmatched: include upstream requirement as-is (new control)\n *\n * Current requirements with no upstream match are dropped by default\n * (a control removed from upstream should not survive the upgrade).\n * Set opts.keepUnmatched to retain them instead.\n */\nexport function generateUpgrade(\n currentBaseline: HDFBaseline,\n upstreamBaseline: HDFBaseline,\n linkRecords: LinkRecord[],\n opts?: UpgradeOptions,\n): UpgradeResult {\n const prefer = opts?.prefer;\n\n // Build lookup: newId -> LinkRecord (prefer primary over related)\n const linkByNewId = new Map<string, LinkRecord>();\n for (const lr of linkRecords) {\n const existing = linkByNewId.get(lr.newId);\n if (!existing || lr.relationship === 'primary') {\n linkByNewId.set(lr.newId, lr);\n }\n }\n\n // Build lookup: oldId -> current requirement\n const currentById = new Map<string, BaselineRequirement>();\n for (const req of currentBaseline.requirements) {\n currentById.set(req.id, req);\n }\n\n // Track which current IDs got matched\n const matchedCurrentIds = new Set<string>();\n\n // Merge upstream requirements\n const mergedReqs: BaselineRequirement[] = [];\n for (const upReq of upstreamBaseline.requirements) {\n const link = linkByNewId.get(upReq.id);\n if (link && link.oldId) {\n const curReq = currentById.get(link.oldId);\n if (curReq) {\n matchedCurrentIds.add(link.oldId);\n\n // Handle --noCode\n const effectiveCurrent = opts?.noCode\n ? { ...curReq, code: undefined }\n : curReq;\n\n mergedReqs.push(mergeRequirement(effectiveCurrent, upReq, prefer));\n continue;\n }\n }\n // Unmatched upstream: include as-is\n mergedReqs.push(upReq);\n }\n\n // Include unmatched current requirements only when explicitly opted in.\n // By default they're dropped — a control removed from upstream should\n // not survive the upgrade, matching SAF CLI delta semantics.\n if (opts?.keepUnmatched) {\n for (const curReq of currentBaseline.requirements) {\n if (!matchedCurrentIds.has(curReq.id)) {\n mergedReqs.push(curReq);\n }\n }\n }\n\n // Build upgraded baseline (use upstream metadata)\n const upgradedBaseline: HDFBaseline = {\n ...upstreamBaseline,\n requirements: mergedReqs,\n };\n\n // Compute statistics\n const statistics = computeStatistics(linkRecords, currentBaseline.requirements.length, upstreamBaseline.requirements.length);\n\n const result: UpgradeResult = {\n baseline: upgradedBaseline,\n linkRecords,\n statistics,\n };\n\n // Generate InSpec profile if requested\n const outputFormat = opts?.outputFormat ?? '';\n if (outputFormat === 'inspec' || outputFormat === 'both' || outputFormat === '') {\n result.profile = generateProfileFromBaseline(upgradedBaseline, opts);\n }\n\n return result;\n}\n\n/**\n * Legacy entry point. Wraps generateUpgrade for backward compatibility.\n *\n * @deprecated Use generateUpgrade instead.\n */\nexport function generateDelta(\n newBaseline: HDFBaseline,\n linkRecords: LinkRecord[],\n oldCodeMap: Map<string, string>,\n opts?: DeltaOptions,\n oldControlCount?: number,\n): DeltaResult {\n // Build synthetic current baseline from code map\n const currentReqs = buildCurrentReqsFromCodeMap(linkRecords, oldCodeMap);\n const currentBaseline: HDFBaseline = {\n name: 'current',\n requirements: currentReqs,\n groups: [],\n supports: [],\n };\n\n const result = generateUpgrade(currentBaseline, newBaseline, linkRecords, opts);\n\n // Patch statistics to use provided old control count\n result.statistics.oldControlsLength = oldControlCount ?? 0;\n\n return result;\n}\n\nfunction buildCurrentReqsFromCodeMap(\n linkRecords: LinkRecord[],\n oldCodeMap: Map<string, string>,\n): BaselineRequirement[] {\n const seen = new Set<string>();\n const reqs: BaselineRequirement[] = [];\n\n for (const lr of linkRecords) {\n if (!lr.oldId || seen.has(lr.oldId)) continue;\n seen.add(lr.oldId);\n\n const req: BaselineRequirement = {\n id: lr.oldId,\n impact: 0,\n tags: {},\n descriptions: [{ label: 'default', data: '' }],\n };\n const code = oldCodeMap.get(lr.oldId);\n if (code) {\n req.code = code;\n }\n reqs.push(req);\n }\n return reqs;\n}\n\nfunction generateProfileFromBaseline(baseline: HDFBaseline, opts?: UpgradeOptions): InSpecProfile {\n const controls = new Map<string, string>();\n const allStubs: string[] = [];\n\n for (const req of baseline.requirements) {\n const ruby = generateControlStub(req);\n\n if (opts?.singleFile) {\n allStubs.push(ruby);\n } else {\n const safeId = req.id.replace(/\\.\\./g, '').replace(/[/\\\\]/g, '') || 'unknown';\n controls.set(`controls/${safeId}.rb`, ruby);\n }\n }\n\n if (opts?.singleFile && allStubs.length > 0) {\n controls.set('controls/controls.rb', allStubs.join('\\n'));\n }\n\n const inspecYml = generateInSpecYml(baseline, {\n singleFile: opts?.singleFile,\n metadata: opts?.metadata,\n inspecVersion: opts?.inspecVersion,\n });\n\n return { inspecYml, controls };\n}\n\nfunction computeStatistics(linkRecords: LinkRecord[], oldControlCount: number, newControlCount: number): DeltaStatistics {\n let match = 0;\n let posMisMatch = 0;\n let dupMatch = 0;\n let noMatch = 0;\n\n for (const lr of linkRecords) {\n if (lr.relationship === 'related') {\n dupMatch++;\n } else if (lr.relationship === 'no-match') {\n noMatch++;\n } else if (lr.potentialMismatch) {\n posMisMatch++;\n } else {\n match++;\n }\n }\n\n return {\n oldControlsLength: oldControlCount,\n newControlsLength: newControlCount,\n totalMappedControls: match + posMisMatch + dupMatch,\n match,\n posMisMatch,\n dupMatch,\n noMatch,\n };\n}\n","import type { DeltaResult, LinkRecord } from './delta-types.js';\n\n/**\n * JSON report payload for delta operations.\n * SAF CLI-compatible: { links: LinkRecord[] }\n */\nexport interface DeltaJsonReport {\n links: LinkRecord[];\n}\n\n/**\n * Generate a structured JSON report from a delta result.\n * Matches SAF CLI's delta.json format: { ...diff, links }.\n */\nexport function generateDeltaJson(result: DeltaResult): DeltaJsonReport {\n return {\n links: result.linkRecords,\n };\n}\n\n/**\n * Format a per-link match method description matching SAF CLI's logMatchMethod.\n */\nfunction formatMatchMethod(lr: LinkRecord): string {\n const confidencePct = (lr.confidence * 100).toFixed(0) + '%';\n switch (lr.matchMethod) {\n case 'srgDeterministic':\n return `SRG deterministic (${lr.srg ?? '?'}) [${lr.relationship}]`;\n case 'srgCciTiebreak':\n return `SRG block + CCI tiebreak (Jaccard=${confidencePct}) [${lr.relationship}]`;\n case 'vendorFuzzyTitle':\n return `Vendor fuzzy title (confidence=${confidencePct}) [${lr.relationship}]`;\n case 'exactId':\n return `Exact ID [${lr.relationship}]`;\n case 'cciMatch':\n return `CCI match [${lr.relationship}]`;\n case 'fuzzyTitle':\n return `Fuzzy title (confidence=${confidencePct}) [${lr.relationship}]`;\n case 'none':\n return 'No match';\n default:\n return `${lr.matchMethod} [${lr.relationship}]`;\n }\n}\n\n/**\n * Generate a Markdown report from a delta result.\n * Matches SAF CLI's delta.md format: mapping table, control counts,\n * match statistics, and statistics validation.\n */\nexport function generateDeltaMarkdown(result: DeltaResult): string {\n const lines: string[] = [];\n const stats = result.statistics;\n\n // Mapping results — SAF CLI format: Old Control -> New Control\n if (result.linkRecords.length > 0) {\n lines.push('Mapping Results ===========================================================================');\n lines.push('\\tOld Control -> New Control');\n for (const lr of result.linkRecords) {\n if (lr.relationship !== 'no-match' && lr.oldId) {\n lines.push(`\\t ${lr.oldId} -> ${lr.newId}`);\n }\n }\n\n const totalMapped = stats.totalMappedControls;\n lines.push(`Total Mapped Controls: ${totalMapped}`);\n lines.push('');\n }\n\n // Control counts\n lines.push('Control Counts ===========================');\n lines.push(`Total Controls Available for Delta: ${stats.oldControlsLength}`);\n lines.push(` Total Controls Found on XCCDF: ${stats.newControlsLength}`);\n lines.push('');\n\n // Match statistics\n lines.push('Match Statistics =========================');\n lines.push(` Match Controls: ${stats.match}`);\n lines.push(` Possible Mismatch Controls: ${stats.posMisMatch}`);\n lines.push(` Related Match Controls: ${stats.dupMatch}`);\n lines.push(` No Match Controls: ${stats.noMatch}`);\n lines.push('');\n\n // Statistics validation\n lines.push('Statistics Validation =============================================');\n const totalMapped = stats.totalMappedControls;\n const matchMappedValid = (stats.match + stats.posMisMatch + stats.dupMatch) === totalMapped;\n const processedValid = (totalMapped + stats.noMatch) === stats.newControlsLength;\n lines.push(`Match + Mismatch + Related = Total Mapped Controls: (${stats.match}+${stats.posMisMatch}+${stats.dupMatch}=${totalMapped}) ${matchMappedValid}`);\n lines.push(` Total Processed = Total XCCDF Controls: (${totalMapped}+${stats.noMatch}=${totalMapped + stats.noMatch}) ${processedValid}`);\n lines.push('');\n\n // Per-control match method details\n if (result.linkRecords.length > 0) {\n lines.push('Match Details =============================================================');\n for (const lr of result.linkRecords) {\n if (lr.oldId) {\n lines.push(` ${lr.oldId} --> ${lr.newId}`);\n lines.push(` Match method: ${formatMatchMethod(lr)}`);\n } else {\n lines.push(` (none) --> ${lr.newId} [no match]`);\n }\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/ruby-escape.ts","../src/control-stub.ts","../src/inspec-yml.ts","../src/profile-generator.ts","../src/merge.ts","../src/delta.ts","../src/delta-report.ts"],"sourcesContent":["/**\n * Escape a string for use as a Ruby string literal.\n *\n * Strategy:\n * - If the string contains both single and double quotes → %q() wrapper\n * - If the string contains only single quotes → double-quoted with escaping\n * - Otherwise → single-quoted with escaping\n *\n * Ported from ts-inspec-objects escapeQuotes(), cross-referenced with\n * inspec-parser's RubyRebuilder.\n */\nexport function escapeQuotes(s: string): string {\n const hasSingle = s.includes(\"'\");\n const hasDouble = s.includes('\"');\n\n if (hasSingle && hasDouble) {\n // %q() — escape backslashes before ) so Ruby doesn't treat \\) as escaped delimiter\n return `%q(${s.replace(/\\\\\\)/g, '\\\\\\\\)')})`;\n }\n\n if (hasSingle) {\n // Double-quoted: escape backslashes, then double quotes\n return `\"${s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n }\n\n // Single-quoted: escape backslashes, then single quotes\n return `'${s.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")}'`;\n}\n","import type { BaselineRequirement } from '@mitre/hdf-schema';\nimport { escapeQuotes } from './ruby-escape.js';\n\n/**\n * Detects when a code string already starts with `control 'ID' do`,\n * meaning it's a complete InSpec control file — not a body fragment.\n */\nconst FULL_CONTROL_BLOCK = /^\\s*control\\s+['\"]([^'\"]+)['\"]\\s+do\\b/;\n\n/**\n * Generate a Ruby InSpec control stub from an HDF BaselineRequirement.\n *\n * Output follows the InSpec DSL ordering convention:\n * control 'ID' do\n * title ...\n * desc ...\n * desc 'check', ...\n * impact ...\n * tag key: value\n * <code or stub comment>\n * end\n */\nexport function generateControlStub(req: BaselineRequirement): string {\n // If code is already a complete `control 'ID' do ... end` block (e.g.\n // from `-c controls/` reading whole .rb files), emit it as-is — wrapping\n // it again would produce nested control blocks, which InSpec rejects.\n // When the inner ID differs from req.id (e.g. an upgrade rename match\n // where current's code was carried into a renamed requirement), rewrite\n // the wrapper to match req.id so the file remains valid InSpec.\n if (req.code) {\n const m = FULL_CONTROL_BLOCK.exec(req.code);\n if (m && m[1] !== undefined) {\n const innerID = m[1];\n if (innerID === req.id) {\n return req.code;\n }\n const start = m.index + m[0].indexOf(innerID);\n return req.code.slice(0, start) + req.id + req.code.slice(start + innerID.length);\n }\n }\n\n const lines: string[] = [];\n\n lines.push(`control '${req.id}' do`);\n\n // Title\n if (req.title) {\n lines.push(` title ${escapeQuotes(req.title)}`);\n }\n\n // Descriptions: default first (as bare `desc`), then labeled\n const defaultDesc = req.descriptions.find((d) => d.label === 'default');\n if (defaultDesc) {\n lines.push(` desc ${escapeQuotes(defaultDesc.data)}`);\n }\n\n const seenDefault = new Set<string>();\n for (const d of req.descriptions) {\n if (d.label === 'default') {\n if (seenDefault.has('default')) {\n continue; // skip duplicate default\n }\n seenDefault.add('default');\n continue; // already emitted above as bare desc\n }\n lines.push(` desc '${d.label}', ${escapeQuotes(d.data)}`);\n }\n\n // Impact — always render with at least one decimal place for 0 and whole numbers\n // RAW impact by design: a generated control declares the impact the profile\n // asserts, not the impact an assessment later resolved it to.\n if (req.impact !== undefined) {\n const impactStr = Number.isInteger(req.impact)\n ? req.impact.toFixed(1)\n : String(req.impact);\n lines.push(` impact ${impactStr}`);\n }\n\n // Tags\n for (const [key, value] of Object.entries(req.tags)) {\n lines.push(` ${formatTag(key, value)}`);\n }\n\n // Code body or stub placeholder\n if (req.code) {\n lines.push('');\n lines.push(req.code);\n } else {\n lines.push('');\n lines.push(' # TODO: Add InSpec test code here');\n }\n\n lines.push('end');\n lines.push(''); // trailing newline\n\n return lines.join('\\n');\n}\n\n/** Format a single tag key-value pair as Ruby DSL. */\nfunction formatTag(key: string, value: unknown): string {\n if (value === null || value === undefined) {\n return `tag ${key}: nil`;\n }\n\n if (Array.isArray(value)) {\n // Ruby array syntax: tag key: ['val1', 'val2']\n const items = value.map((v: unknown) => `'${String(v)}'`).join(', ');\n return `tag ${key}: [${items}]`;\n }\n\n if (typeof value === 'boolean') {\n return `tag ${key}: ${value}`;\n }\n\n if (typeof value === 'string') {\n return `tag ${key}: ${escapeQuotes(value)}`;\n }\n\n // Fallback for other types (numbers, objects)\n return `tag ${key}: ${JSON.stringify(value)}`;\n}\n","import type { HDFBaseline } from '@mitre/hdf-schema';\nimport type { GeneratorOptions } from './types.js';\n\n/**\n * Generate an inspec.yml YAML string from an HDF Baseline and options.\n *\n * Uses string interpolation — no YAML library needed since the structure\n * is fixed and shallow.\n */\nexport function generateInSpecYml(\n baseline: HDFBaseline,\n options?: GeneratorOptions,\n): string {\n const lines: string[] = [];\n const meta = options?.metadata;\n\n // Required: name\n lines.push(`name: ${baseline.name}`);\n\n // Optional metadata fields\n if (baseline.title) {\n lines.push(`title: ${baseline.title}`);\n }\n\n const maintainer = meta?.maintainer ?? baseline.maintainer;\n if (maintainer) {\n lines.push(`maintainer: ${maintainer}`);\n }\n\n const copyright = meta?.copyright ?? baseline.copyright;\n if (copyright) {\n lines.push(`copyright: ${copyright}`);\n }\n\n const license = meta?.license ?? baseline.license;\n if (license) {\n lines.push(`license: ${license}`);\n }\n\n if (baseline.summary) {\n lines.push(`summary: ${baseline.summary}`);\n }\n\n // Version: metadata override takes priority\n const version = meta?.version ?? baseline.version;\n if (version) {\n lines.push(`version: '${version}'`);\n }\n\n // InSpec version constraint\n const inspecVersion = options?.inspecVersion ?? '~>6.0';\n lines.push(`inspec_version: '${inspecVersion}'`);\n\n // Supports array\n if (baseline.supports && baseline.supports.length > 0) {\n lines.push('supports:');\n for (const support of baseline.supports) {\n const entries: string[] = [];\n if (support.platformName) {\n entries.push(` platform-name: ${support.platformName}`);\n }\n if (support.platformFamily) {\n entries.push(` platform-family: ${support.platformFamily}`);\n }\n if (support.platform) {\n entries.push(` platform: ${support.platform}`);\n }\n if (support.release) {\n entries.push(` release: ${support.release}`);\n }\n if (entries.length > 0) {\n lines.push(`- ${entries[0]!.trimStart()}`);\n for (let i = 1; i < entries.length; i++) {\n lines.push(entries[i]!);\n }\n }\n }\n }\n\n // Depends array\n if (baseline.depends && baseline.depends.length > 0) {\n lines.push('depends:');\n for (const dep of baseline.depends) {\n const entries: string[] = [];\n if (dep.name) entries.push(`name: ${dep.name}`);\n if (dep.git) entries.push(`git: ${dep.git}`);\n if (dep.url) entries.push(`url: ${dep.url}`);\n if (dep.path) entries.push(`path: ${dep.path}`);\n if (dep.branch) entries.push(`branch: ${dep.branch}`);\n if (dep.compliance) entries.push(`compliance: ${dep.compliance}`);\n if (dep.supermarket) entries.push(`supermarket: ${dep.supermarket}`);\n if (entries.length > 0) {\n lines.push(`- ${entries[0]}`);\n for (let i = 1; i < entries.length; i++) {\n lines.push(` ${entries[i]}`);\n }\n }\n }\n }\n\n // Inputs array\n if (baseline.inputs && baseline.inputs.length > 0) {\n lines.push('inputs:');\n for (const input of baseline.inputs) {\n for (const [key, value] of Object.entries(input)) {\n lines.push(`- ${key}: ${formatYamlValue(value)}`);\n }\n }\n }\n\n lines.push(''); // trailing newline\n return lines.join('\\n');\n}\n\n/** Format a value for inline YAML output. */\nfunction formatYamlValue(value: unknown): string {\n if (typeof value === 'boolean') return String(value);\n if (typeof value === 'number') return String(value);\n if (typeof value === 'string') return value;\n return JSON.stringify(value);\n}\n","import type { BaselineRequirement, HDFBaseline } from '@mitre/hdf-schema';\nimport type { GeneratorOptions, InSpecProfile } from './types.js';\nimport { generateControlStub } from './control-stub.js';\nimport { generateInSpecYml } from './inspec-yml.js';\n\n/**\n * Generate an in-memory InSpec profile from an HDF Baseline.\n *\n * Returns an InSpecProfile with inspec.yml content and a Map of\n * control filenames to Ruby source code. No file I/O — the CLI\n * is responsible for writing files to disk.\n */\nexport function generateInSpecProfile(\n baseline: HDFBaseline,\n options?: GeneratorOptions,\n): InSpecProfile {\n const inspecYml = generateInSpecYml(baseline, options);\n const controls = new Map<string, string>();\n\n if (baseline.requirements.length === 0) {\n return { inspecYml, controls };\n }\n\n if (options?.singleFile) {\n // All controls in a single file\n const stubs = baseline.requirements.map((req: BaselineRequirement) => generateControlStub(req));\n controls.set('controls/controls.rb', stubs.join('\\n'));\n } else {\n // One file per control — sanitize ID for safe filenames\n for (const req of baseline.requirements) {\n const safeId = req.id.replace(/\\.\\./g, '').replace(/[/\\\\]/g, '') || 'unknown';\n const filename = `controls/${safeId}.rb`;\n controls.set(filename, generateControlStub(req));\n }\n }\n\n return { inspecYml, controls };\n}\n","import type { BaselineRequirement, Description, Reference } from '@mitre/hdf-schema';\n\nexport type PreferSide = 'current' | 'upstream' | undefined;\n\n/**\n * Smart-merge a current and upstream requirement.\n *\n * Default (no prefer):\n * - ID: always upstream\n * - Scalars (title, impact, severity): upstream wins\n * - Tags: union, upstream wins key conflicts\n * - Descriptions: union by label, upstream wins on same label\n * - Code: current (preserve tests)\n * - Refs: union (deduplicated)\n *\n * prefer \"current\": scalars from current, current wins tag/desc conflicts\n * prefer \"upstream\": everything from upstream (full replacement)\n */\nexport function mergeRequirement(\n current: BaselineRequirement,\n upstream: BaselineRequirement,\n prefer?: PreferSide,\n): BaselineRequirement {\n const merged: BaselineRequirement = {\n // ID always comes from upstream\n id: upstream.id,\n\n // Scalars\n title: prefer === 'current' ? current.title : upstream.title,\n impact: prefer === 'current' ? current.impact : upstream.impact,\n severity: prefer === 'current' ? current.severity : upstream.severity,\n\n // Collections\n tags: mergeTags(current.tags, upstream.tags, prefer),\n descriptions: mergeDescriptions(current.descriptions, upstream.descriptions, prefer),\n refs: mergeRefs(current.refs, upstream.refs, prefer),\n\n // Code: current by default, upstream only with --prefer upstream\n code: prefer === 'upstream'\n ? upstream.code\n : (current.code ?? upstream.code),\n\n // SourceLocation follows scalars\n sourceLocation: prefer === 'current' ? current.sourceLocation : upstream.sourceLocation,\n };\n\n // Clean up undefined optional fields\n if (merged.code === undefined) delete merged.code;\n if (merged.severity === undefined) delete merged.severity;\n if (merged.sourceLocation === undefined) delete merged.sourceLocation;\n if (merged.refs === undefined || merged.refs.length === 0) delete merged.refs;\n\n return merged;\n}\n\n/**\n * Merge two tag maps.\n *\n * Default: union of keys; upstream wins on key conflicts.\n * prefer \"current\": union; current wins on key conflicts.\n * prefer \"upstream\": upstream replaces all.\n */\nexport function mergeTags(\n current: Record<string, any>, // eslint-disable-line @typescript-eslint/no-explicit-any\n upstream: Record<string, any>, // eslint-disable-line @typescript-eslint/no-explicit-any\n prefer?: PreferSide,\n): Record<string, any> { // eslint-disable-line @typescript-eslint/no-explicit-any\n if (prefer === 'upstream') {\n return { ...upstream };\n }\n\n const merged: Record<string, any> = { ...current }; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n for (const [key, value] of Object.entries(upstream)) {\n if (prefer === 'current') {\n if (!(key in merged)) {\n merged[key] = value;\n }\n } else {\n // Default: upstream wins on conflict\n merged[key] = value;\n }\n }\n\n return merged;\n}\n\n/**\n * Merge two description arrays by label.\n *\n * Default: union by label; upstream wins on label conflicts.\n * prefer \"current\": union; current wins on label conflicts.\n * prefer \"upstream\": upstream replaces all.\n */\nexport function mergeDescriptions(\n current: Description[],\n upstream: Description[],\n prefer?: PreferSide,\n): Description[] {\n if (prefer === 'upstream') {\n return [...upstream];\n }\n\n const byLabel = new Map<string, Description>();\n const order: string[] = [];\n\n // Start with current\n for (const d of current) {\n byLabel.set(d.label, d);\n order.push(d.label);\n }\n\n // Apply upstream\n for (const d of upstream) {\n if (byLabel.has(d.label)) {\n if (prefer !== 'current') {\n // Default: upstream wins\n byLabel.set(d.label, d);\n }\n } else {\n byLabel.set(d.label, d);\n order.push(d.label);\n }\n }\n\n return order.map(label => byLabel.get(label)!);\n}\n\n/**\n * Merge two reference arrays.\n *\n * Default: union, deduplicated by JSON key.\n * prefer \"current\": current only.\n * prefer \"upstream\": upstream only.\n */\nexport function mergeRefs(\n current?: Reference[],\n upstream?: Reference[],\n prefer?: PreferSide,\n): Reference[] | undefined {\n if (prefer === 'current') {\n return current ? [...current] : undefined;\n }\n if (prefer === 'upstream') {\n return upstream ? [...upstream] : undefined;\n }\n\n // Default: union, deduplicated\n if (!current && !upstream) return undefined;\n\n const seen = new Set<string>();\n const result: Reference[] = [];\n\n const addRef = (r: Reference): void => {\n const key = JSON.stringify(r);\n if (!seen.has(key)) {\n seen.add(key);\n result.push(r);\n }\n };\n\n if (current) current.forEach(addRef);\n if (upstream) upstream.forEach(addRef);\n\n return result.length > 0 ? result : undefined;\n}\n","import type { HDFBaseline, BaselineRequirement } from '@mitre/hdf-schema';\nimport type { UpgradeResult, UpgradeOptions, DeltaResult, DeltaOptions, LinkRecord, DeltaStatistics } from './delta-types.js';\nimport type { InSpecProfile } from './types.js';\nimport { mergeRequirement } from './merge.js';\nimport { generateControlStub } from './control-stub.js';\nimport { generateInSpecYml } from './inspec-yml.js';\n\n/**\n * Generate an upgraded HDF Baseline by smart-merging current and upstream requirements.\n *\n * For each upstream requirement:\n * - If matched: smart-merge current + upstream fields per mergeRequirement semantics\n * - If unmatched: include upstream requirement as-is (new control)\n *\n * Current requirements with no upstream match are dropped by default\n * (a control removed from upstream should not survive the upgrade).\n * Set opts.keepUnmatched to retain them instead.\n */\nexport function generateUpgrade(\n currentBaseline: HDFBaseline,\n upstreamBaseline: HDFBaseline,\n linkRecords: LinkRecord[],\n opts?: UpgradeOptions,\n): UpgradeResult {\n const prefer = opts?.prefer;\n\n // Build lookup: newId -> LinkRecord (prefer primary over related)\n const linkByNewId = new Map<string, LinkRecord>();\n for (const lr of linkRecords) {\n const existing = linkByNewId.get(lr.newId);\n if (!existing || lr.relationship === 'primary') {\n linkByNewId.set(lr.newId, lr);\n }\n }\n\n // Build lookup: oldId -> current requirement\n const currentById = new Map<string, BaselineRequirement>();\n for (const req of currentBaseline.requirements) {\n currentById.set(req.id, req);\n }\n\n // Track which current IDs got matched\n const matchedCurrentIds = new Set<string>();\n\n // Merge upstream requirements\n const mergedReqs: BaselineRequirement[] = [];\n for (const upReq of upstreamBaseline.requirements) {\n const link = linkByNewId.get(upReq.id);\n if (link && link.oldId) {\n const curReq = currentById.get(link.oldId);\n if (curReq) {\n matchedCurrentIds.add(link.oldId);\n\n // Handle --noCode\n const effectiveCurrent = opts?.noCode\n ? { ...curReq, code: undefined }\n : curReq;\n\n mergedReqs.push(mergeRequirement(effectiveCurrent, upReq, prefer));\n continue;\n }\n }\n // Unmatched upstream: include as-is\n mergedReqs.push(upReq);\n }\n\n // Include unmatched current requirements only when explicitly opted in.\n // By default they're dropped — a control removed from upstream should\n // not survive the upgrade, matching SAF CLI delta semantics.\n if (opts?.keepUnmatched) {\n for (const curReq of currentBaseline.requirements) {\n if (!matchedCurrentIds.has(curReq.id)) {\n mergedReqs.push(curReq);\n }\n }\n }\n\n // Build upgraded baseline (use upstream metadata)\n const upgradedBaseline: HDFBaseline = {\n ...upstreamBaseline,\n requirements: mergedReqs,\n };\n\n // Compute statistics\n const statistics = computeStatistics(linkRecords, currentBaseline.requirements.length, upstreamBaseline.requirements.length);\n\n const result: UpgradeResult = {\n baseline: upgradedBaseline,\n linkRecords,\n statistics,\n };\n\n // Generate InSpec profile if requested\n const outputFormat = opts?.outputFormat ?? '';\n if (outputFormat === 'inspec' || outputFormat === 'both' || outputFormat === '') {\n result.profile = generateProfileFromBaseline(upgradedBaseline, opts);\n }\n\n return result;\n}\n\n/**\n * Legacy entry point. Wraps generateUpgrade for backward compatibility.\n *\n * @deprecated Use generateUpgrade instead.\n */\nexport function generateDelta(\n newBaseline: HDFBaseline,\n linkRecords: LinkRecord[],\n oldCodeMap: Map<string, string>,\n opts?: DeltaOptions,\n oldControlCount?: number,\n): DeltaResult {\n // Build synthetic current baseline from code map\n const currentReqs = buildCurrentReqsFromCodeMap(linkRecords, oldCodeMap);\n const currentBaseline: HDFBaseline = {\n name: 'current',\n requirements: currentReqs,\n groups: [],\n supports: [],\n };\n\n const result = generateUpgrade(currentBaseline, newBaseline, linkRecords, opts);\n\n // Patch statistics to use provided old control count\n result.statistics.oldControlsLength = oldControlCount ?? 0;\n\n return result;\n}\n\nfunction buildCurrentReqsFromCodeMap(\n linkRecords: LinkRecord[],\n oldCodeMap: Map<string, string>,\n): BaselineRequirement[] {\n const seen = new Set<string>();\n const reqs: BaselineRequirement[] = [];\n\n for (const lr of linkRecords) {\n if (!lr.oldId || seen.has(lr.oldId)) continue;\n seen.add(lr.oldId);\n\n const req: BaselineRequirement = {\n id: lr.oldId,\n impact: 0,\n tags: {},\n descriptions: [{ label: 'default', data: '' }],\n };\n const code = oldCodeMap.get(lr.oldId);\n if (code) {\n req.code = code;\n }\n reqs.push(req);\n }\n return reqs;\n}\n\nfunction generateProfileFromBaseline(baseline: HDFBaseline, opts?: UpgradeOptions): InSpecProfile {\n const controls = new Map<string, string>();\n const allStubs: string[] = [];\n\n for (const req of baseline.requirements) {\n const ruby = generateControlStub(req);\n\n if (opts?.singleFile) {\n allStubs.push(ruby);\n } else {\n const safeId = req.id.replace(/\\.\\./g, '').replace(/[/\\\\]/g, '') || 'unknown';\n controls.set(`controls/${safeId}.rb`, ruby);\n }\n }\n\n if (opts?.singleFile && allStubs.length > 0) {\n controls.set('controls/controls.rb', allStubs.join('\\n'));\n }\n\n const inspecYml = generateInSpecYml(baseline, {\n singleFile: opts?.singleFile,\n metadata: opts?.metadata,\n inspecVersion: opts?.inspecVersion,\n });\n\n return { inspecYml, controls };\n}\n\nfunction computeStatistics(linkRecords: LinkRecord[], oldControlCount: number, newControlCount: number): DeltaStatistics {\n let match = 0;\n let posMisMatch = 0;\n let dupMatch = 0;\n let noMatch = 0;\n\n for (const lr of linkRecords) {\n if (lr.relationship === 'related') {\n dupMatch++;\n } else if (lr.relationship === 'no-match') {\n noMatch++;\n } else if (lr.potentialMismatch) {\n posMisMatch++;\n } else {\n match++;\n }\n }\n\n return {\n oldControlsLength: oldControlCount,\n newControlsLength: newControlCount,\n totalMappedControls: match + posMisMatch + dupMatch,\n match,\n posMisMatch,\n dupMatch,\n noMatch,\n };\n}\n","import type { DeltaResult, LinkRecord } from './delta-types.js';\n\n/**\n * JSON report payload for delta operations.\n * SAF CLI-compatible: { links: LinkRecord[] }\n */\nexport interface DeltaJsonReport {\n links: LinkRecord[];\n}\n\n/**\n * Generate a structured JSON report from a delta result.\n * Matches SAF CLI's delta.json format: { ...diff, links }.\n */\nexport function generateDeltaJson(result: DeltaResult): DeltaJsonReport {\n return {\n links: result.linkRecords,\n };\n}\n\n/**\n * Format a per-link match method description matching SAF CLI's logMatchMethod.\n */\nfunction formatMatchMethod(lr: LinkRecord): string {\n const confidencePct = (lr.confidence * 100).toFixed(0) + '%';\n switch (lr.matchMethod) {\n case 'srgDeterministic':\n return `SRG deterministic (${lr.srg ?? '?'}) [${lr.relationship}]`;\n case 'srgCciTiebreak':\n return `SRG block + CCI tiebreak (Jaccard=${confidencePct}) [${lr.relationship}]`;\n case 'vendorFuzzyTitle':\n return `Vendor fuzzy title (confidence=${confidencePct}) [${lr.relationship}]`;\n case 'exactId':\n return `Exact ID [${lr.relationship}]`;\n case 'cciMatch':\n return `CCI match [${lr.relationship}]`;\n case 'fuzzyTitle':\n return `Fuzzy title (confidence=${confidencePct}) [${lr.relationship}]`;\n case 'none':\n return 'No match';\n default:\n return `${lr.matchMethod} [${lr.relationship}]`;\n }\n}\n\n/**\n * Generate a Markdown report from a delta result.\n * Matches SAF CLI's delta.md format: mapping table, control counts,\n * match statistics, and statistics validation.\n */\nexport function generateDeltaMarkdown(result: DeltaResult): string {\n const lines: string[] = [];\n const stats = result.statistics;\n\n // Mapping results — SAF CLI format: Old Control -> New Control\n if (result.linkRecords.length > 0) {\n lines.push('Mapping Results ===========================================================================');\n lines.push('\\tOld Control -> New Control');\n for (const lr of result.linkRecords) {\n if (lr.relationship !== 'no-match' && lr.oldId) {\n lines.push(`\\t ${lr.oldId} -> ${lr.newId}`);\n }\n }\n\n const totalMapped = stats.totalMappedControls;\n lines.push(`Total Mapped Controls: ${totalMapped}`);\n lines.push('');\n }\n\n // Control counts\n lines.push('Control Counts ===========================');\n lines.push(`Total Controls Available for Delta: ${stats.oldControlsLength}`);\n lines.push(` Total Controls Found on XCCDF: ${stats.newControlsLength}`);\n lines.push('');\n\n // Match statistics\n lines.push('Match Statistics =========================');\n lines.push(` Match Controls: ${stats.match}`);\n lines.push(` Possible Mismatch Controls: ${stats.posMisMatch}`);\n lines.push(` Related Match Controls: ${stats.dupMatch}`);\n lines.push(` No Match Controls: ${stats.noMatch}`);\n lines.push('');\n\n // Statistics validation\n lines.push('Statistics Validation =============================================');\n const totalMapped = stats.totalMappedControls;\n const matchMappedValid = (stats.match + stats.posMisMatch + stats.dupMatch) === totalMapped;\n const processedValid = (totalMapped + stats.noMatch) === stats.newControlsLength;\n lines.push(`Match + Mismatch + Related = Total Mapped Controls: (${stats.match}+${stats.posMisMatch}+${stats.dupMatch}=${totalMapped}) ${matchMappedValid}`);\n lines.push(` Total Processed = Total XCCDF Controls: (${totalMapped}+${stats.noMatch}=${totalMapped + stats.noMatch}) ${processedValid}`);\n lines.push('');\n\n // Per-control match method details\n if (result.linkRecords.length > 0) {\n lines.push('Match Details =============================================================');\n for (const lr of result.linkRecords) {\n if (lr.oldId) {\n lines.push(` ${lr.oldId} --> ${lr.newId}`);\n lines.push(` Match method: ${formatMatchMethod(lr)}`);\n } else {\n lines.push(` (none) --> ${lr.newId} [no match]`);\n }\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mitre/hdf-generators",
3
- "version": "3.7.1-rc.1",
3
+ "version": "3.7.2-rc.1",
4
4
  "description": "Generate InSpec profile stubs from HDF Baseline definitions",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -25,13 +25,13 @@
25
25
  "author": "MITRE Corporation",
26
26
  "license": "Apache-2.0",
27
27
  "dependencies": {
28
- "@mitre/hdf-schema": "^3.7.1-rc.1"
28
+ "@mitre/hdf-schema": "^3.7.2-rc.1"
29
29
  },
30
30
  "devDependencies": {
31
- "@mitre/hdf-fixtures": "^3.7.1-rc.1"
31
+ "@mitre/hdf-fixtures": "^3.7.2-rc.1"
32
32
  },
33
33
  "engines": {
34
- "node": ">=22.0.0"
34
+ "node": ">=24.0.0"
35
35
  },
36
36
  "keywords": [
37
37
  "hdf",