|
| 1 | +/** Windows Script Host runs this source directly using ES3-compatible JScript. */ |
| 2 | +/** |
| 3 | + * Treat the target as a filesystem path, not a registry key or share. |
| 4 | + * @see https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_pathtype_enum |
| 5 | + */ |
| 6 | +var ADS_PATH_FILE = 1; |
| 7 | +/** |
| 8 | + * Use the ADSI descriptor object so its DACL can be replaced. |
| 9 | + * @see https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_sd_format_enum |
| 10 | + */ |
| 11 | +var ADS_SD_FORMAT_IID = 1; |
| 12 | +/** |
| 13 | + * Read and write only the DACL, preserving ownership and audit settings. |
| 14 | + * @see https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_security_info_enum |
| 15 | + */ |
| 16 | +var ADS_SECURITY_INFO_DACL = 4; |
| 17 | +/** |
| 18 | + * Revision 2 supports the ordinary, non-object ACEs used for this file. |
| 19 | + * @see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-acl |
| 20 | + */ |
| 21 | +var ACL_REVISION = 2; |
| 22 | +/** |
| 23 | + * Grant full file access to the three permitted trustees. |
| 24 | + * @see https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants |
| 25 | + */ |
| 26 | +var FILE_ALL_ACCESS = 0x1f01ff; |
| 27 | +/** |
| 28 | + * Numeric SID syntax avoids localized account-name resolution. |
| 29 | + * @see https://learn.microsoft.com/en-us/windows/win32/secauthz/sid-components |
| 30 | + */ |
| 31 | +var USER_SID_PATTERN = /^S-\d+(?:-\d+)+$/; |
| 32 | + |
| 33 | +try { |
| 34 | + if (WScript.Arguments.length !== 2) { |
| 35 | + throw new Error("Expected a file path and user SID"); |
| 36 | + } |
| 37 | + var target = WScript.Arguments.Item(0); |
| 38 | + var sid = WScript.Arguments.Item(1); |
| 39 | + if (!USER_SID_PATTERN.test(sid)) { |
| 40 | + throw new Error("Invalid Windows user SID"); |
| 41 | + } |
| 42 | + |
| 43 | + // Replace the file DACL through built-in ADSI without changing its owner. |
| 44 | + // https://learn.microsoft.com/en-us/windows/win32/api/iads/nn-iads-iadssecurityutility |
| 45 | + var security = new ActiveXObject("ADsSecurityUtility"); |
| 46 | + security.SecurityMask = ADS_SECURITY_INFO_DACL; |
| 47 | + var descriptor = security.GetSecurityDescriptor( |
| 48 | + target, |
| 49 | + ADS_PATH_FILE, |
| 50 | + ADS_SD_FORMAT_IID |
| 51 | + ); |
| 52 | + var dacl = new ActiveXObject("AccessControlList"); |
| 53 | + dacl.AclRevision = ACL_REVISION; |
| 54 | + // Permit the current user, SYSTEM, and the local Administrators group. |
| 55 | + // https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids |
| 56 | + var trustees = [sid, "S-1-5-18", "S-1-5-32-544"]; |
| 57 | + // Keep indexed iteration: this source is not transpiled, and ES3 has no for...of. |
| 58 | + for (var index = 0; index < trustees.length; index++) { |
| 59 | + var ace = new ActiveXObject("AccessControlEntry"); |
| 60 | + ace.Trustee = trustees[index]; |
| 61 | + ace.AccessMask = FILE_ALL_ACCESS; |
| 62 | + // An allow ACE with no inheritance flags applies only to this file. |
| 63 | + // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header |
| 64 | + ace.AceType = 0; |
| 65 | + ace.AceFlags = 0; |
| 66 | + dacl.AddAce(ace); |
| 67 | + } |
| 68 | + descriptor.DiscretionaryAcl = dacl; |
| 69 | + security.SetSecurityDescriptor( |
| 70 | + target, |
| 71 | + ADS_PATH_FILE, |
| 72 | + descriptor, |
| 73 | + ADS_SD_FORMAT_IID |
| 74 | + ); |
| 75 | +} catch (error) { |
| 76 | + /** |
| 77 | + * Automation errors carry `description` rather than `message`. |
| 78 | + * @see https://learn.microsoft.com/en-us/openspecs/ie_standards/ms-es3ex/a4f75a6b-dda5-40e1-85d0-9f95afb24fba |
| 79 | + * @type {any} |
| 80 | + */ |
| 81 | + var thrown = error; |
| 82 | + WScript.StdErr.WriteLine( |
| 83 | + "Windows ACL repair failed: " + |
| 84 | + ((thrown && (thrown.message || thrown.description)) || thrown) |
| 85 | + ); |
| 86 | + WScript.Quit(1); |
| 87 | +} |
0 commit comments