Skip to content

Commit 40e2091

Browse files
fix: harden ownership context imports (#361)
1 parent 0ed047d commit 40e2091

13 files changed

Lines changed: 344 additions & 79 deletions

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ For the fuller explanation, use the [visual field guide](docs/visual-field-guide
3131

3232
For the first real run:
3333

34-
1. Download `ShareSurfer-0.1.0-pre.32.zip` and its SHA256 file from the [current prerelease](https://github.com/jonathanweinberg/ShareSurfer/releases/tag/v0.1.0-pre.32). If that tag is not visible, use the latest published prerelease and substitute its version in the paths below.
35-
2. Extract to `C:\` so the release root is `C:\ShareSurfer-0.1.0-pre.32\`.
34+
1. Download `ShareSurfer-0.1.0-pre.33.zip` and its SHA256 file from the [current prerelease](https://github.com/jonathanweinberg/ShareSurfer/releases/tag/v0.1.0-pre.33). If that tag is not visible, use the latest published prerelease and substitute its version in the paths below.
35+
2. Extract to `C:\` so the release root is `C:\ShareSurfer-0.1.0-pre.33\`.
3636
3. Recursively unblock extracted PowerShell files:
3737

3838
```powershell
39-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
39+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
4040
Get-ChildItem -LiteralPath $releaseRoot -Recurse -File |
4141
Where-Object { $_.Extension -in '.ps1', '.psm1', '.psd1' } |
4242
Unblock-File
@@ -149,7 +149,7 @@ See the [nonpermissive collector to dashboard host workflow](docs/nonpermissive-
149149
Use this compact pattern when the release folder has been copied to a locked-down Windows collector host:
150150

151151
```powershell
152-
$shareSurferRoot = 'C:\ShareSurfer-0.1.0-pre.32'
152+
$shareSurferRoot = 'C:\ShareSurfer-0.1.0-pre.33'
153153
$exportPath = 'C:\ShareSurfer\exports\scan-001'
154154
$handoffPath = 'C:\ShareSurfer\handoff\scan-001.zip'
155155
$inputRoot = 'C:\ShareSurfer\inputs'
@@ -232,7 +232,7 @@ Current screenshots are under [docs/visuals/dashboard-screenshots/2026-06-09-cur
232232

233233
## Pre-1.0 Release Packaging
234234

235-
The first packages are unsigned but fully built. `v0.1.0-pre.32` includes the module, scripts, docs, SHA256 files, release manifest, dependency-age report, and prebuilt dashboard template assets. The manifest records `UnsignedPre1.0`.
235+
The first packages are unsigned but fully built. `v0.1.0-pre.33` includes the module, scripts, docs, SHA256 files, release manifest, dependency-age report, and prebuilt dashboard template assets. The manifest records `UnsignedPre1.0`.
236236

237237
Release identity lives in [release-metadata.json](release-metadata.json). Update that file first when preparing a prerelease; packaging fails closed when the manual version or tag does not match.
238238

docs/admin-ownership-import.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ Enriched rows can add these review fields:
133133

134134
## Step 1: Test The CSV
135135

136-
If `v0.1.0-pre.32` is not visible yet on the [ShareSurfer Releases page](https://github.com/jonathanweinberg/ShareSurfer/releases), use the latest published prerelease and substitute that version in `$releaseRoot`.
136+
If `v0.1.0-pre.33` is not visible yet on the [ShareSurfer Releases page](https://github.com/jonathanweinberg/ShareSurfer/releases), use the latest published prerelease and substitute that version in `$releaseRoot`.
137137

138138
```powershell
139-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
139+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
140140
$sourcePath = 'C:\ShareSurfer\inputs\hr-obs.csv'
141141
142142
Import-Module "$releaseRoot\src\ShareSurfer\ShareSurfer.psd1" -Force
@@ -281,7 +281,7 @@ If you already saved mapping profiles for the same files, pass them with `-Mappi
281281

282282
During a multi-source join, ShareSurfer now prints progress in phases: selected CSVs, source rows being processed, merged ownership rows, OBS context merge, AD lookup attempts, output files, and the final matched/source-only/ambiguous counts. Long AD-backed imports should continue to print a heartbeat instead of sitting silently after the header interview.
283283

284-
Large project, OBS, path, or group context files can legitimately create many `ownership_context.csv` and `ownership_relationships.csv` rows. Current optimized builds aggregate OBS-only context and apply it by OBS bucket instead of merging every context row into every matching identity as the file is read. If a large context import is crawling for hours, stop the older run and move to the optimized release before trying again.
284+
Large project, OBS, path, or group context files can legitimately create many `ownership_context.csv` and `ownership_relationships.csv` rows. Current optimized builds aggregate OBS-only context and apply it by OBS bucket instead of merging every context row into every matching identity as the file is read. They also keep source-path, source-row, and warning provenance in a set-backed working structure so one heavily reused OBS bucket does not spend hours repeatedly splitting and rejoining the same CSV-cell text. If a large context import is crawling for hours, stop the older run and move to the optimized release before trying again.
285285

286286
If an older ShareSurfer run has been silent for a long time, it is safe to stop it with `Ctrl+C` and rerun with the saved `ownership-import.definition.json` or `ownership-enrichment-rerun.ps1`. The import reads CSVs and AD, then writes local output files; it does not modify AD, shares, or permissions.
287287

docs/command-recipes.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ This page collects the most common first-run commands in one place. Use it when
55
The examples assume the current quickstart release is unpacked here:
66

77
```text
8-
C:\ShareSurfer-0.1.0-pre.32\
8+
C:\ShareSurfer-0.1.0-pre.33\
99
```
1010

11-
If `v0.1.0-pre.32` is not visible yet on the [ShareSurfer Releases page](https://github.com/jonathanweinberg/ShareSurfer/releases), use the latest published prerelease and substitute that version in every `ShareSurfer-0.1.0-pre.32` path and ZIP name below. The commands also assume Windows PowerShell 5.1 unless a command explicitly says otherwise.
11+
If `v0.1.0-pre.33` is not visible yet on the [ShareSurfer Releases page](https://github.com/jonathanweinberg/ShareSurfer/releases), use the latest published prerelease and substitute that version in every `ShareSurfer-0.1.0-pre.33` path and ZIP name below. The commands also assume Windows PowerShell 5.1 unless a command explicitly says otherwise.
1212

1313
## Start Here
1414

@@ -49,11 +49,11 @@ If `v0.1.0-pre.32` is not visible yet on the [ShareSurfer Releases page](https:/
4949

5050
## Recipe 1: Unpack and Import the Release
5151

52-
Use this on the Windows collector host after downloading `ShareSurfer-0.1.0-pre.32.zip` from the GitHub release on an approved connected workstation. If that checkpoint ZIP is not published yet, download the latest published prerelease ZIP and update `$releaseZip` and `$releaseRoot` to match it.
52+
Use this on the Windows collector host after downloading `ShareSurfer-0.1.0-pre.33.zip` from the GitHub release on an approved connected workstation. If that checkpoint ZIP is not published yet, download the latest published prerelease ZIP and update `$releaseZip` and `$releaseRoot` to match it.
5353

5454
```powershell
55-
$releaseZip = 'C:\Downloads\ShareSurfer-0.1.0-pre.32.zip'
56-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
55+
$releaseZip = 'C:\Downloads\ShareSurfer-0.1.0-pre.33.zip'
56+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
5757
5858
Expand-Archive -LiteralPath $releaseZip -DestinationPath 'C:\' -Force
5959
Get-ChildItem -LiteralPath $releaseRoot -Recurse -File |
@@ -71,7 +71,7 @@ The `Unblock-File` line clears the Windows downloaded-file block from ShareSurfe
7171

7272
Run that manual unblock first for the no-prompt path. If you start `Start-ShareSurfer.ps1` before unblocking, Windows may still ask once for the launcher itself; after you choose **Run once**, the launcher attempts the same recursive unblock before importing ShareSurfer.
7373

74-
Both `Test-Path` commands should return `True`. If either returns `False`, check for a doubled folder such as `C:\ShareSurfer-0.1.0-pre.32\ShareSurfer-0.1.0-pre.32`.
74+
Both `Test-Path` commands should return `True`. If either returns `False`, check for a doubled folder such as `C:\ShareSurfer-0.1.0-pre.33\ShareSurfer-0.1.0-pre.33`.
7575

7676
## Recipe 1A: Generate a Guided Startup Plan
7777

@@ -84,7 +84,7 @@ If `ownership-enrichment.csv` is missing, interactive startup can offer to launc
8484
The easiest release-root launcher is:
8585

8686
```powershell
87-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
87+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
8888
$inputRoot = 'C:\ShareSurfer\inputs'
8989
$exportPath = 'C:\ShareSurfer\exports\finance-001'
9090
@@ -94,7 +94,7 @@ $exportPath = 'C:\ShareSurfer\exports\finance-001'
9494
If you already know the answers and want to generate the same files without prompts, import the module and call the startup command directly:
9595

9696
```powershell
97-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
97+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
9898
$inputRoot = 'C:\ShareSurfer\inputs'
9999
$exportPath = 'C:\ShareSurfer\exports\finance-001'
100100
@@ -172,7 +172,7 @@ If you do not have either file yet, leave it absent. The scan recipes below only
172172
Use this when another team gives you a CSV with useful owner or OBS data but the headers do not match ShareSurfer's expected owner mapping format.
173173

174174
```powershell
175-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
175+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
176176
$sourcePath = 'C:\ShareSurfer\inputs\hr-obs.csv'
177177
$profilePath = 'C:\ShareSurfer\inputs\hr-obs.mapping.json'
178178
$normalizedPath = 'C:\ShareSurfer\inputs\normalized-ownership.csv'
@@ -211,7 +211,7 @@ If you need ShareSurfer to ask you about each header in the console, add `-Inter
211211
To gather AD data from an HR or OBS file before scanning, create an enrichment CSV. ShareSurfer uses employee ID or employee number values from the source CSV to look up matching AD accounts when `-AdLookupMode Auto` or `ActiveDirectory` can read the directory. It fills available account, mail, title, office, manager, and OBS fields, then writes a local CSV that travels with the scan evidence.
212212

213213
```powershell
214-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
214+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
215215
$inputRoot = 'C:\ShareSurfer\inputs'
216216
$ownershipEnrichmentPath = Join-Path $inputRoot 'ownership-enrichment.csv'
217217
$ownershipDefinitionPath = Join-Path $inputRoot 'ownership-import.definition.json'
@@ -291,7 +291,7 @@ For more detail, see the [admin ownership import guide](admin-ownership-import.m
291291
Use this when you already know the share path and want a first reviewable export.
292292

293293
```powershell
294-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
294+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
295295
$exportPath = 'C:\ShareSurfer\exports\finance-001'
296296
$ownerMappingPath = 'C:\ShareSurfer\inputs\owner-mapping.csv'
297297
$ownershipEnrichmentPath = 'C:\ShareSurfer\inputs\ownership-enrichment.csv'
@@ -332,7 +332,7 @@ Use this recipe first if you are new to the tool. It can still record partial-da
332332
Use this when you know the Windows file server and share name and want ShareSurfer to collect share metadata.
333333

334334
```powershell
335-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
335+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
336336
$exportPath = 'C:\ShareSurfer\exports\finance-001'
337337
338338
Import-Module "$releaseRoot\src\ShareSurfer\ShareSurfer.psd1" -Force
@@ -354,7 +354,7 @@ Use `-IncludeFiles` only when file-level rows matter for the review. Large share
354354
Use this when a Windows SMB target is reachable but default remote CIM or SMB cmdlets cannot prove share metadata cleanly.
355355

356356
```powershell
357-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
357+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
358358
$exportPath = 'C:\ShareSurfer\exports\finance-native-001'
359359
360360
Import-Module "$releaseRoot\src\ShareSurfer\ShareSurfer.psd1" -Force
@@ -379,7 +379,7 @@ When `NetShareGetInfo` returns a path like `C:\Public\Share` for a remote target
379379
Use this when ShareSurfer can reach a share but `share_permissions.csv` is empty, `shares.csv` says `PartialData=True`, or `collection_errors.csv` shows share-permission or native security descriptor failures. This goes past "is the port open" and records each share-permission proof attempt.
380380

381381
```powershell
382-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
382+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
383383
$diagnosticPath = 'C:\ShareSurfer\diagnostics\finance-share-permissions'
384384
385385
Import-Module "$releaseRoot\src\ShareSurfer\ShareSurfer.psd1" -Force
@@ -403,7 +403,7 @@ For a support-safe packet, review and share the redacted files under `$diagnosti
403403
Run this after the collector finishes.
404404

405405
```powershell
406-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
406+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
407407
$exportPath = 'C:\ShareSurfer\exports\finance-001'
408408
409409
Test-ShareSurferExport -ExportPath $exportPath
@@ -430,7 +430,7 @@ Before owner signoff, open `evidence_confidence.csv` or the dashboard Scan Confi
430430
Use this after a scan has produced `owner_review_packets.csv` and `related_data_areas.csv`. The draft files are plain CSVs that can be edited in Excel, reviewed in a meeting, and imported back into the export folder before rebuilding the report or standalone dashboard.
431431

432432
```powershell
433-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
433+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
434434
$exportPath = 'C:\ShareSurfer\exports\finance-001'
435435
$decisionPath = 'C:\ShareSurfer\reviews\finance-001'
436436
$decisionRerunPath = Join-Path $decisionPath 'review-decisions-rerun.ps1'

docs/first-run-guide.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ If report or command terms are unfamiliar, keep the [glossary](glossary.md) open
88

99
For a first useful scan:
1010

11-
1. Extract the current `v0.1.0-pre.32` release ZIP to `C:\`. If that checkpoint tag is not visible on the [ShareSurfer Releases page](https://github.com/jonathanweinberg/ShareSurfer/releases) yet, use the latest published prerelease and substitute that version in the paths below.
12-
2. Use `C:\ShareSurfer-0.1.0-pre.32\` as `$releaseRoot`, or replace the version folder with the published prerelease you actually extracted.
11+
1. Extract the current `v0.1.0-pre.33` release ZIP to `C:\`. If that checkpoint tag is not visible on the [ShareSurfer Releases page](https://github.com/jonathanweinberg/ShareSurfer/releases) yet, use the latest published prerelease and substitute that version in the paths below.
12+
2. Use `C:\ShareSurfer-0.1.0-pre.33\` as `$releaseRoot`, or replace the version folder with the published prerelease you actually extracted.
1313
3. Run the recursive `Unblock-File` command in Step 1 before importing the module.
1414
4. Pick one known share and the correct `-ObsAttribute`.
1515
5. Recommended: run `Start-ShareSurfer.ps1` or `Start-ShareSurferStartup` to generate a reusable first-run JSON config, plan, and rerun script.
@@ -92,17 +92,17 @@ $PSVersionTable.PSVersion
9292

9393
The major version should be `5`.
9494

95-
If you are using the `v0.1.0-pre.32` release ZIP, extract it to `C:\`. If that checkpoint tag is not visible yet, use the latest published prerelease and substitute that version in the paths below. The extracted release root should be:
95+
If you are using the `v0.1.0-pre.33` release ZIP, extract it to `C:\`. If that checkpoint tag is not visible yet, use the latest published prerelease and substitute that version in the paths below. The extracted release root should be:
9696

9797
```text
98-
C:\ShareSurfer-0.1.0-pre.32\
98+
C:\ShareSurfer-0.1.0-pre.33\
9999
```
100100

101-
If Windows Explorer suggests extracting to `C:\ShareSurfer-0.1.0-pre.32`, change the destination to `C:\` so you do not end up with a doubled nested folder. From PowerShell:
101+
If Windows Explorer suggests extracting to `C:\ShareSurfer-0.1.0-pre.33`, change the destination to `C:\` so you do not end up with a doubled nested folder. From PowerShell:
102102

103103
```powershell
104-
$releaseZip = 'C:\Downloads\ShareSurfer-0.1.0-pre.32.zip'
105-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
104+
$releaseZip = 'C:\Downloads\ShareSurfer-0.1.0-pre.33.zip'
105+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
106106
107107
Expand-Archive -LiteralPath $releaseZip -DestinationPath 'C:\' -Force
108108
Get-ChildItem -LiteralPath $releaseRoot -Recurse -File |
@@ -716,12 +716,12 @@ For the longer version, see the [nonpermissive collector to dashboard host workf
716716

717717
## Optional: Generate the Standalone Dashboard
718718

719-
The legacy `report.html` remains the safest default report because it is generated directly by the PowerShell module. The v0.1.0-pre.32 release package from the [ShareSurfer Releases page](https://github.com/jonathanweinberg/ShareSurfer/releases), or the latest published prerelease while waiting for that checkpoint tag to appear, also includes prebuilt standalone dashboard template assets for richer novice-admin and business-owner review.
719+
The legacy `report.html` remains the safest default report because it is generated directly by the PowerShell module. The v0.1.0-pre.33 release package from the [ShareSurfer Releases page](https://github.com/jonathanweinberg/ShareSurfer/releases), or the latest published prerelease while waiting for that checkpoint tag to appear, also includes prebuilt standalone dashboard template assets for richer novice-admin and business-owner review.
720720

721721
If you are using the release ZIP, you do not need Node, npm, Vite, a development server, or internet access to package the dashboard. Run the packager from Windows PowerShell 5.1 and point it at the extracted release root:
722722

723723
```powershell
724-
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.32'
724+
$releaseRoot = 'C:\ShareSurfer-0.1.0-pre.33'
725725
726726
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "$releaseRoot\scripts\New-ShareSurferStandaloneDashboard.ps1" `
727727
-ExportPath $exportPath `

docs/first-run-troubleshooting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Also do not treat a high confidence label as permission approval. Confidence is
4040
| Normalized ownership rows show `PotentialServiceAccount=True` | The row has no OBS, no employee ID, and no employee number after import. | Treat it as a review clue, not proof. It may be a service account, automation account, shared account, or incomplete HR/directory record. |
4141
| `owner-mapping-draft.csv` has blank owners | The draft is a starter file, not a completed owner mapping. | Fill in `Owner` and `BusinessUnit`, save it as `owner-mapping.csv`, and rerun the scan with `-OwnerMappingPath`. |
4242
| `Discounted principals file was not found` | `-DiscountedPrincipalPath` was passed, but the CSV does not exist. | Remove the parameter until the file exists, or create `discounted-principals.csv` with at least an `Identity` column. |
43-
| `Test-Path "$releaseRoot\src\ShareSurfer\ShareSurfer.psd1"` returns `False` | The release ZIP may have been extracted into a doubled folder such as `C:\ShareSurfer-0.1.0-pre.32\ShareSurfer-0.1.0-pre.32`. | Move the inner release folder up one level, or extract the ZIP again to `C:\` so `$releaseRoot` points at `C:\ShareSurfer-0.1.0-pre.32`. |
43+
| `Test-Path "$releaseRoot\src\ShareSurfer\ShareSurfer.psd1"` returns `False` | The release ZIP may have been extracted into a doubled folder such as `C:\ShareSurfer-0.1.0-pre.33\ShareSurfer-0.1.0-pre.33`. | Move the inner release folder up one level, or extract the ZIP again to `C:\` so `$releaseRoot` points at `C:\ShareSurfer-0.1.0-pre.33`. |
4444
| WinRM or CIM cannot connect | The target does not allow the remote management route used for share metadata and share permissions. | ShareSurfer tries native SMB/RPC share-permission evidence when the target is a UNC share and `Get-SmbShareAccess` cannot return rows. If scanning by `-ComputerName` and `-ShareName`, you can also force `-SmbCollectionProvider NativeSmbRpc`. If native evidence still cannot prove share permissions, treat share-level data as partial and review `collection_errors.csv`. |
4545
| SMB/RPC port checks pass but native security descriptor errors appear | The network route is open, but the collector account or SMB server did not return usable Windows owner, DACL, or share security descriptor evidence. | Review `NativeShareSecurityDescriptorUnavailable`, `NativeShareSecurityDescriptorParseFailed`, `NativeSecurityDescriptorReadFailed`, and `NativeSecurityDescriptorParseFailed` rows in `collection_errors.csv`. Confirm the account can read security details, try an elevated/admin token when allowed, and review Samba or appliance NT ACL compatibility when the target is not Windows. |
4646
| Access denied, unauthorized operation, or path denied | The collector account cannot read part of the share, ACL, owner, security descriptor, or open-file provider. | Rerun from an elevated Windows PowerShell prompt when allowed. Confirm the account can read the path and security details with normal Windows tools. Review Diagnostics before treating the scan as complete. |

0 commit comments

Comments
 (0)