Open-source engine for parsing and analyzing Microsoft Group Policy XML exports.
Zero dependencies. Pure TypeScript. Runs anywhere — browser, Node, Deno, Bun.
Powers GPO Audit — the Group Policy audit tool for sysadmins, MSPs, and security teams.
- Parse GPO XML exports from
Get-GPOReport -ReportType Xml - Detect conflicts — same setting, different values across GPOs, with precedence resolution
- Find duplicates — identical settings repeated across multiple GPOs
- Flag issues — empty, disabled, and unlinked GPOs
- CIS Benchmark compliance — 29 rules covering password policy, account lockout, Kerberos, audit policy, and Windows Firewall (mapped to NIST 800-53)
- Redact sensitive data — strip domains, IPs, SIDs, paths, and email addresses before sharing or storing results
npm install @gpoaudit/parserimport { parseGpoXml, analyzeGpos, evaluateCompliance, redactAudit } from '@gpoaudit/parser';
// 1. Parse XML (from Get-GPOReport or GPMC export)
const xmlString = fs.readFileSync('gpo-report.xml', 'utf-8');
const gpos = parseGpoXml(xmlString);
// 2. Analyze for issues
const audit = analyzeGpos(gpos);
console.log(`Total GPOs: ${audit.total}`);
console.log(`Conflicts: ${audit.conflicts.length}`);
console.log(`Duplicates: ${audit.duplicates.length}`);
console.log(`Empty: ${audit.emptyGpos.length}`);
console.log(`Disabled: ${audit.disabledGpos.length}`);
console.log(`Unlinked: ${audit.unlinkedGpos.length}`);
// 3. Check CIS Benchmark compliance
const compliance = evaluateCompliance(audit);
console.log(`Compliance: ${compliance.scorePercent}% (${compliance.passed}/${compliance.totalRules})`);
// 4. Redact sensitive data before sharing
const redacted = redactAudit(audit);
// Safe to send to an API, store in a database, or paste into ChatGPTRun this in PowerShell on a domain controller or machine with RSAT:
# All GPOs in the domain
Get-GPOReport -All -ReportType Xml -Path "AllGPOs.xml"
# Single GPO
Get-GPOReport -Name "Default Domain Policy" -ReportType Xml -Path "DefaultPolicy.xml"| Function | Description |
|---|---|
parseGpoXml(xml: string) |
Parse XML string into Gpo[] |
parseGpoXmlBytes(buffer: ArrayBuffer) |
Parse raw bytes (handles UTF-16 BOM) |
analyzeGpos(gpos: Gpo[]) |
Analyze for conflicts, duplicates, and issues → Audit |
| Function | Description |
|---|---|
evaluateCompliance(audit: Audit) |
Evaluate against CIS Benchmark rules → ComplianceSummary |
CATEGORY_ORDER |
Display order for compliance categories |
| Function | Description |
|---|---|
redactAudit(audit: Audit) |
Strip sensitive data from full audit → RedactedAudit |
redactString(input: string) |
Redact a single string value |
The redaction module replaces:
| Pattern | Replacement |
|---|---|
UNC paths (\\server\share) |
[REDACTED-PATH] |
File paths (C:\Users\...) |
[REDACTED-PATH] |
Windows SIDs (S-1-5-21-...) |
[REDACTED-SID] |
FQDNs (host.example.com) |
[REDACTED-HOST] |
| IPv4 addresses | [REDACTED-IP] |
| Email addresses | [REDACTED-EMAIL] |
| Domain names (auto-detected) | [REDACTED-DOMAIN] |
29 CIS Benchmark rules across 5 categories, each mapped to NIST 800-53 controls:
| Category | Rules | Examples |
|---|---|---|
| Password Policy | 6 | Min length ≥ 14, complexity, history ≥ 24 |
| Account Lockout | 4 | Threshold 1–5, duration ≥ 15 min |
| Kerberos Policy | 3 | Ticket lifetime, clock skew |
| Audit Policy | 10 | Logon, account mgmt, process tracking |
| Firewall | 6 | Domain/Private/Public profiles enabled, inbound block |
When the same setting exists in multiple GPOs with different values, the parser determines which GPO "wins" using Active Directory precedence rules:
- Enforced (No Override) GPOs always win (+1000 precedence)
- Deeper OU links override parent OU links (child OU = more specific = wins)
- The winning entry is marked with
isApplied: true
Works in any modern browser — no Node.js required:
<input type="file" id="upload" accept=".xml" />
<script type="module">
import { parseGpoXml, analyzeGpos } from '@gpoaudit/parser';
document.getElementById('upload').addEventListener('change', async (e) => {
const text = await e.target.files[0].text();
const gpos = parseGpoXml(text);
const audit = analyzeGpos(gpos);
console.log(audit);
});
</script>Contributions welcome! Especially:
- New compliance rules — STIG, DISA, custom frameworks
- Parser improvements — additional extension namespaces
- Bug reports — XML exports that don't parse correctly
MIT — see LICENSE.
Built by JACrystal · Powering gpoaudit.com