{"_id":"@a-kazemi/sppa","_rev":"2-04799c763f42b7a7be73147d5dc309a5","name":"@a-kazemi/sppa","dist-tags":{"latest":"0.2.1"},"versions":{"0.2.0":{"name":"@a-kazemi/sppa","version":"0.2.0","keywords":["sharepoint","sharepoint-server","sharepoint-on-premises","sharepoint-2016","sharepoint-2019","subscription-edition","permissions","permission-audit","security","governance","access-review","ntlm","cli"],"author":{"name":"Amir Kazemi"},"license":"MIT","_id":"@a-kazemi/sppa@0.2.0","maintainers":[{"name":"a-kazemi","email":"kazemi1@outlook.com"}],"homepage":"https://github.com/a-kazemi/sppa#readme","bugs":{"url":"https://github.com/a-kazemi/sppa/issues"},"bin":{"sppa":"dist/src/index.js"},"dist":{"shasum":"26e7763187a9a705f69309c6ffe44bcde69d12a1","tarball":"https://registry.npmjs.org/@a-kazemi/sppa/-/sppa-0.2.0.tgz","fileCount":29,"integrity":"sha512-8anOaL0Bnq3A4KBC1xmJj+WObsIgbqcX5uWFEdjcJvPnxJF7uuTjBCkdVUPEHNp3FR9koCR6evPQP1Tx86ygiw==","signatures":[{"sig":"MEYCIQDR1x5h0aXJKtAMIq0boPBf0WTceiNlHRkO2hcXSoO1nQIhAORo1rtQgJy+Lz5PFLoNhMH2AjWBLVWaJJ6xWIiuRaYq","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":154763},"main":"dist/src/index.js","type":"commonjs","engines":{"node":">=18"},"gitHead":"b63577bf8a22b0aa8ceb0eca0e7da52518b237d9","scripts":{"test":"npm run build && node --test dist/test/*.test.js","build":"tsc -p tsconfig.json","clean":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"","prepare":"npm run build","test:only":"node --test dist/test/*.test.js","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"a-kazemi","email":"kazemi1@outlook.com"},"repository":{"url":"git+https://github.com/a-kazemi/sppa.git","type":"git"},"_npmVersion":"11.16.0","description":"Explain and audit SharePoint Server on-premises permissions from the command line. Read-only, no data egress, no telemetry.","directories":{},"_nodeVersion":"24.18.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0","@types/node":"^20.14.0"},"_npmOperationalInternal":{"tmp":"tmp/sppa_0.2.0_1788467592144_0.6321562305465938","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@a-kazemi/sppa","version":"0.2.1","description":"Explain and audit SharePoint Server on-premises permissions from the command line. Read-only, no data egress, no telemetry.","bin":{"sppa":"dist/src/index.js"},"main":"dist/src/index.js","type":"commonjs","engines":{"node":">=18"},"scripts":{"build":"tsc -p tsconfig.json","prepare":"npm run build","test":"npm run build && node --test dist/test/*.test.js","test:only":"node --test dist/test/*.test.js","clean":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"","prepublishOnly":"npm run clean && npm run build"},"keywords":["sharepoint","sharepoint-server","sharepoint-on-premises","sharepoint-2016","sharepoint-2019","subscription-edition","permissions","permission-audit","security","governance","access-review","ntlm","cli"],"author":{"name":"Amir Kazemi"},"license":"MIT","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/a-kazemi/sppa.git"},"bugs":{"url":"https://github.com/a-kazemi/sppa/issues"},"homepage":"https://github.com/a-kazemi/sppa#readme","devDependencies":{"@types/node":"^20.14.0","typescript":"^5.6.0"},"gitHead":"ee99c61404796df5a3e8781644e56afbcf0bd86f","_id":"@a-kazemi/sppa@0.2.1","_nodeVersion":"24.18.1","_npmVersion":"11.16.0","dist":{"integrity":"sha512-74y3WSGaDQeJ056y/VHakAlJrnDb5PNh3+OHkqBvckpLVZbwf4bd3pycWkmjnypeuZD4FHY4PYl8EB30Tzr/ow==","shasum":"c77da05ee3a05582a383b99f4480faac6e9ecdf2","tarball":"https://registry.npmjs.org/@a-kazemi/sppa/-/sppa-0.2.1.tgz","fileCount":30,"unpackedSize":163744,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC4QvtmJyNdLiWp1VpC/+iGXn0wENCII9ekrJEE/CGv9QIgAm/yTu0UCqMW2Ac5Z1CY4u6T1FVtuTuEpHR8cQ/5zzk="}]},"_npmUser":{"name":"a-kazemi","email":"kazemi1@outlook.com"},"directories":{},"maintainers":[{"name":"a-kazemi","email":"kazemi1@outlook.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sppa_0.2.1_1788498944196_0.288697180931043"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-03T20:33:11.964Z","modified":"2026-09-04T05:15:44.515Z","0.2.0":"2026-09-03T20:33:12.311Z","0.2.1":"2026-09-04T05:15:44.369Z"},"bugs":{"url":"https://github.com/a-kazemi/sppa/issues"},"author":{"name":"Amir Kazemi"},"license":"MIT","homepage":"https://github.com/a-kazemi/sppa#readme","keywords":["sharepoint","sharepoint-server","sharepoint-on-premises","sharepoint-2016","sharepoint-2019","subscription-edition","permissions","permission-audit","security","governance","access-review","ntlm","cli"],"repository":{"type":"git","url":"git+https://github.com/a-kazemi/sppa.git"},"description":"Explain and audit SharePoint Server on-premises permissions from the command line. Read-only, no data egress, no telemetry.","maintainers":[{"name":"a-kazemi","email":"kazemi1@outlook.com"}],"readme":"# sppa\n\n[![CI](https://github.com/a-kazemi/sppa/actions/workflows/ci.yml/badge.svg)](https://github.com/a-kazemi/sppa/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nExplain and audit **SharePoint Server on-premises** permissions from the command\nline. Built for SharePoint Server 2016, 2019 and Subscription Edition farms.\n\n- **`explain-access`** — answer \"why does this user have access to this site?\"\n  in one command: direct grants, SharePoint group membership, broad-audience\n  claims (\"Everyone\"), AD security groups, and exactly where inheritance breaks.\n- **`list-access`** — the inverse: \"who has access to this site or list, and\n  how?\" SharePoint groups are expanded to their members; AD security groups and\n  broad audiences are flagged as unexpandable.\n- **`scan-site`** — audit one site collection (add `--recurse` for subwebs) for\n  broken permission inheritance, orphaned SIDs (deleted AD accounts still on\n  ACLs), broad-audience grants, oversized SharePoint groups, and site collection\n  administrators.\n\n**Read-only. No data leaves your machine. No telemetry. No account required.**\nThe tool only issues `GET` requests to the SharePoint REST API (`_api`).\n\n> Status: `v0.2.1`, early release. It does one job on classic NTLM farms. If it\n> is useful — or if it breaks in your environment — please\n> [open an issue](https://github.com/a-kazemi/sppa/issues).\n\n---\n\n## Install\n\nRequires **Node.js 18 or newer**.\n\n```bash\nnpm install -g @a-kazemi/sppa\nsppa --help\n```\n\nOr run it without installing:\n\n```bash\nnpx @a-kazemi/sppa scan-site --site https://sharepoint/sites/hr\n```\n\nFrom source (for development):\n\n```bash\ngit clone https://github.com/a-kazemi/sppa.git\ncd sppa\nnpm install && npm run build\nnpm link            # puts `sppa` on your PATH\n```\n\n## Authentication\n\nThe `v0.2.x` line supports **NTLM with explicit credentials only** (see [docs/AUTH.md](docs/AUTH.md);\nauth failures are catalogued in [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)).\nProvide the auditing account through environment variables — never on the command\nline, where it would show up in the process list:\n\n```bash\n# macOS / Linux (bash, zsh)\nexport SPPA_USERNAME='CONTOSO\\svc_audit'\nexport SPPA_PASSWORD='...'\n# optional; also parsed from DOMAIN\\user above\nexport SPPA_DOMAIN='CONTOSO'\n```\n\n```powershell\n# Windows — PowerShell\n$env:SPPA_USERNAME = 'CONTOSO\\svc_audit'\n$env:SPPA_PASSWORD = '...'\n$env:SPPA_DOMAIN   = 'CONTOSO'\n```\n\n```bat\nREM Windows — Command Prompt (cmd.exe); do not quote the values\nset SPPA_USERNAME=CONTOSO\\svc_audit\nset SPPA_PASSWORD=...\nset SPPA_DOMAIN=CONTOSO\n```\n\n`export` is Unix-only — on Windows use `set` (cmd.exe) or `$env:` (PowerShell).\nSee [docs/AUTH.md](docs/AUTH.md) for details.\n\nA read-only account works for everything except item-level scanning inside lists\nwhere it lacks access — those items are simply skipped.\n\n## Usage\n\n### Explain why a user has access\n\n```bash\nsppa explain-access --site https://sharepoint/sites/hr --user 'CONTOSO\\a.kazemi'\n```\n\n```\nSharePoint access explanation\nSite: https://sharepoint/sites/hr\nUser: Jane Doe (User, id 14)\nLogin: i:0#.w|contoso\\a.kazemi\n\nVerdict: HAS ACCESS\n\nEffective permissions on this scope:\n  • Open the site\n  • View pages\n  • View list items / documents\n  • Edit list items\n\nInheritance is broken at:\n  • list: Salary Review\n\nHow access is granted:\n  ✓ SharePoint group \"HR Site Members\" — Contribute @ list \"Salary Review\"\n\nNotes:\n  • 1 AD security group(s) on this scope could not be expanded via REST.\n    The effective-permission check above is still authoritative.\n```\n\nScope it to a single list, or handle classic Windows-claims logins:\n\n```bash\nsppa explain-access --site https://sharepoint/sites/hr \\\n  --user 'CONTOSO\\a.kazemi' --list 'Salary Review' --windows-claims\n```\n\n### List who has access\n\n```bash\nsppa list-access --site https://sharepoint/sites/hr\nsppa list-access --site https://sharepoint/sites/hr --list 'Salary Review'\n```\n\n`list-access` reads the role assignments on the scope (or the parent it inherits\nfrom), expands every SharePoint group to its members, and reports AD security\ngroups and broad audiences (\"Everyone\") as single entries it cannot expand. See\n[`sample/list-access.txt`](sample/list-access.txt) for the shape of the output.\n\n### Audit a site collection\n\n```bash\nsppa scan-site --site https://sharepoint/sites/hr\nsppa scan-site --site https://sharepoint/sites/hr --recurse\nsppa scan-site --site https://sharepoint/sites/hr --format json > hr-audit.json\n```\n\n`scan-site` walks the web, every visible list, and (unless `--skip-items`) list\nitems with unique permissions. Add `--recurse` to walk sub-sites too — every\nfinding is then tagged with the subweb it came from and the summary reports how\nmany webs were scanned. Use `--max-items` to bound very large libraries and\n`--large-group-threshold` to tune the oversized-group flag.\n\nUnless `--format json` is used, `scan-site` also writes a standalone HTML report\n(inline CSS, no assets, prints cleanly to PDF) and prints a `Report:` link to it\nas the last line of the terminal output. The default path is\n`./sppa-scan-<host>-<timestamp>.html`; override it with `--report <path>` or turn\nit off with `--no-report`. To re-render a report from a saved\n`--format json` file without re-scanning, pipe it through\n[`scripts/scan-report.mjs`](scripts/scan-report.mjs).\n\nNTLM authenticates one TCP connection, so by default every request is serial.\nOn a large site collection, `--concurrency 4` (say) opens four authenticated\nconnections and runs the scan several times faster. The client also retries\nfarm throttling (`429` / `503`, honouring `Retry-After`) and transient socket\ndrops automatically.\n\n## Worked example\n\nNot ready to point it at a real farm yet? [`sample/`](sample/) contains a\nsynthetic SharePoint Server 2019 site collection — recorded `_api` responses plus\nthe exact `scan-site` and `explain-access` output the tool produces from them,\nshowing every finding (broken inheritance at web/list/item, an orphaned SID, an\n`Everyone` grant, an unexpandable AD group, an oversized group, a site collection\nadmin). It also has both an **ALLOW** trace (`explain-access.txt`) and a **DENY**\ntrace scoped to a single list (`explain-access-deny.txt`). A test regenerates it\nfrom the fixtures on every run, so it never drifts from the code.\n\nFor a line-by-line walk through that sample output — the ALLOW trace, the DENY\ntrace, and `scan-site` — see [docs/GUIDE-explain-access.md](docs/GUIDE-explain-access.md).\n\n## JSON output\n\nBoth commands accept `--format json` and emit a stable envelope\n(`schemaVersion: 1`) suitable for diffing between runs or feeding into a report:\n\n```json\n{\n  \"tool\": \"sppa\",\n  \"schemaVersion\": 1,\n  \"command\": \"scan-site\",\n  \"generatedAt\": \"2026-09-03T12:00:00.000Z\",\n  \"site\": \"https://sharepoint/sites/hr\",\n  \"result\": { \"summary\": { \"...\": \"...\" }, \"brokenInheritance\": [] }\n}\n```\n\n## What it does **not** do (yet)\n\n- No AD FS / WS-Federation or Forms-Based Auth (NTLM only).\n- No Kerberos-only endpoints.\n- No AD security-group expansion — SharePoint REST does not expose it. The tool\n  reports which AD groups are on each ACL and relies on\n  `getUserEffectivePermissions` (which resolves them server-side) for the verdict.\n- No writes, ever. It will not fix anything it finds.\n- No multi-farm, scheduled snapshots, or drift diffing.\n\nWhere those gaps sit in the queue — and everything else that has been asked for —\nis written down in [docs/ROADMAP.md](docs/ROADMAP.md). Listing something there is\nnot a commitment to build it; open an issue to make the case for what you need.\n\n## Exit codes\n\n| Code | Meaning |\n|-----:|---------|\n| 0 | Success |\n| 2 | Usage error (bad flags / missing arguments) |\n| 3 | Authentication failed |\n| 4 | SharePoint reachable but the request failed |\n| 5 | Network / TLS / DNS failure |\n\nEvery non-zero exit prints one `error:` line and usually a hint.\n[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) lists each message with its\ncause and fix — including cases with no dedicated message (clock skew, channel\nbinding / Extended Protection, `_api` disabled, reverse proxies, multi-WFE\naffinity).\n\n## Security\n\nSee [docs/SECURITY.md](docs/SECURITY.md). Short version: read-only, single farm,\nno outbound connections other than to the `--site` you pass, credentials read\nfrom the environment and never logged.\n\n## Feedback wanted — and a free permission audit\n\nThis is an early release and the fastest way to make it better is to hear from\npeople running real farms.\n\n- **Hit a bug or an auth failure?** [Open an issue](https://github.com/a-kazemi/sppa/issues/new/choose)\n  with your SharePoint version and the (redacted) error — the\n  *Authentication failure report* form is the one we most want filled in right now.\n- **Have a messy permission situation you'd like a second pair of eyes on?**\n  Use the [*free permission audit*](https://github.com/a-kazemi/sppa/issues/new/choose)\n  form (no data required) and we'll help you read the `explain-access` /\n  `scan-site` output and figure out what to fix — free, no strings. We're doing\n  this to learn which problems matter most.\n\n## Development\n\n```bash\nnpm install\nnpm run build\nnpm test          # builds, then runs the node:test suite\n```\n\nThe permission-analysis logic (`src/analysis/`) and the NTLM handshake\n(`src/auth/`) are pure functions unit-tested against recorded REST fixtures and\nthe [MS-NLMP] test vectors — no live farm needed to hack on them.\n`test/sample.test.ts` runs the whole `_api` → parse → analyse → render pipeline\nagainst the synthetic farm in [`sample/`](sample/) and fails if the committed\noutput there goes stale.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for what is in and out of scope — `v0.2.x`\nis feature-frozen; bug fixes, docs, tests, and auth reports are what move it\nforward.\n\n## License\n\nMIT \n","readmeFilename":"README.md"}