Direct answer: Check a PowerShell script before running it by preserving the original text, confirming what it is meant to do, parsing it without execution, reviewing static-rule findings in the context where you intend to use it, revising the script, and scanning it again. A clean static result narrows known issues. It does not prove that the script will work correctly, behave safely, or produce the intended result when executed.
PowerShell scripts often operate on files, services, remote systems, cloud resources, or configuration. That makes “does it parse?” an important question, but not the only one. A useful pre-run review separates three decisions:
- Is the text valid PowerShell syntax?
- Does static inspection find patterns that deserve attention?
- What still needs a controlled runtime test?
PSRafScan supports the first two decisions. It reviews an existing script file or clipboard content with the native PowerShell parser and PSRafScan-owned rules. Its analysis does not intentionally execute the target script. The third decision remains yours.
What checking before execution can - and cannot - tell you
| A pre-run check can help you find | A pre-run check cannot establish |
|---|---|
| Parser errors and their source locations | Whether every runtime branch behaves as intended |
| Rule findings tied to an issue and suggested fix | Whether required modules, accounts, files, services, or network endpoints exist |
| Paste-sensitive patterns when the intended use is a live terminal | Whether the chosen terminal host will behave identically to every other host |
| Error-handling patterns that may hide failures | Whether credentials and permissions are sufficient |
| High-impact command patterns that need closer review | Whether a script is free of malicious behavior or safe to run |
| Findings at or below a selected reporting threshold | Whether a passing threshold means there are no findings |
This distinction matters. Parseability is not runtime correctness. A static finding is not automatically a vulnerability. A passing scan is not a safety guarantee.
Layer 1: let the native PowerShell parser inspect the file
PowerShell exposes a parser API that reads a file and returns three useful outputs: tokens, parse errors, and a ScriptBlockAst. The AST - short for Abstract Syntax Tree - is PowerShell’s structured representation of the script. Microsoft documents this behavior in Parser.ParseFile.
PSRafScan calls that native parser against the target file. It normalizes each parser error as a finding with:
- source
Parser - rule name
PowerShellParseError - category
Syntax - severity
Error - line and column information when the parser supplies an extent
- the parser’s issue text and a recommendation to correct the syntax
Parsing reads and analyzes the source unit; it does not intentionally run the target script. If parsing fails, fix the syntax before treating later analysis as complete.
Worked example 1: a missing closing brace
This minimal example comes from the preserved PSRafScan parser self-test fixture:
if ($true) { Write-Output "missing close brace"RSX is the packaged alias for Invoke-PSRafScan; the longer command name can be used in the same places below. Save the text as ParserError.ps1, then scan the file:
RSX -Path ./ParserError.ps1 -IntendedUse Ps1File -FailOn ErrorObserved with PowerShell 7.4.17 and 7.6.3 on Ubuntu 24.04.3: process result 2, status ParseError, and a PowerShellParseError Error at line 1, column 12. Both runtimes reported Missing closing '}' in statement block or type definition. Parser wording remains version-specific.
Layer 2: review PSRafScan-owned rule findings
A script can parse successfully and still contain patterns worth examining. After parser analysis, PSRafScan evaluates its own rule files and normalizes the results into the same report model. A normalized finding can include its source, rule, severity, category, line, column, matched text, issue, and suggested fix.
PSRafScan is separate from PSScriptAnalyzer. It does not call, wrap, or depend on PSScriptAnalyzer. Microsoft describes PSScriptAnalyzer as a static code checker that applies PowerShell Team and community best-practice rules; that is a useful tool with its own rules and contract. See the official PSScriptAnalyzer overview. Do not interpret a PSRafScan finding as a PSScriptAnalyzer result - or the reverse.
At the reviewed PSRafScan source snapshot, 58 rules are active and 67 rule self-test definitions are present. Those counts describe repository commit c9d2a8588d042eef021d43edc2e4586318df1482; they are not permanent product totals.
Worked example 2: an error that disappears from view
A source-derived example adapted from the SilentErrorAction self-test uses a unique, verified-absent temporary path:
$missingPath = Join-Path ([System.IO.Path]::GetTempPath()) "PSRafScan-DefinitelyMissing-$([guid]::NewGuid().ToString('N'))"
if (Test-Path -LiteralPath $missingPath) {
throw "Fixture setup failed: expected a missing path, but it exists: $missingPath"
}
Get-ChildItem -LiteralPath $missingPath -ErrorAction SilentlyContinueMicrosoft’s common-parameter documentation says that -ErrorAction SilentlyContinue suppresses a non-terminating error message and continues execution. That may be deliberate, but it can also make a failure easy to miss. See about_CommonParameters.
Scan the saved file:
RSX -Path ./SilentErrorAction.ps1 -IntendedUse Ps1File -FailOn WarningObserved with PowerShell 7.4.17 and 7.6.3 on Ubuntu 24.04.3: one targeted SilentErrorAction Warning and process result 1. The original candidate was scanned as text and was not executed.
One possible revision makes the failure explicit:
$missingPath = Join-Path ([System.IO.Path]::GetTempPath()) "PSRafScan-DefinitelyMissing-$([guid]::NewGuid().ToString('N'))"
if (Test-Path -LiteralPath $missingPath) {
throw "Fixture setup failed: expected a missing path, but it exists: $missingPath"
}
try {
Get-ChildItem -LiteralPath $missingPath -ErrorAction Stop
}
catch {
Write-Error "Could not inspect '$missingPath': $($_.Exception.Message)"
}This revision removed the SilentlyContinue pattern and rescanned with process result 0 and no SilentErrorAction finding in both tested Linux runtimes. A controlled missing-path check emitted an explicit error containing the unique test path. That result does not prove the broader script is correct; review the full report and test context.
Worked example 3: review a recursive forced deletion as text
Do not execute the first sample. It is an intentionally non-executed review example whose target is built under the operating system's temporary directory. The command would still be destructive if run. The task here is to scan the text, not perform the deletion.
$target = Join-Path ([System.IO.Path]::GetTempPath()) 'PSRafScanArticleDemo'
Remove-Item -LiteralPath $target -Recurse -ForceScan it with:
RSX -Path ./RecursiveForceDelete.ps1 -IntendedUse Ps1File -FailOn WarningObserved with PowerShell 7.4.17 and 7.6.3 on Ubuntu 24.04.3: a RecursiveForceDelete Error and process result 1. The destructive sample was never executed. The reviewed rule looks for Remove-Item with both -Recurse and -Force when -WhatIf is absent.
A review-oriented revision adds path checks and a preview request:
$target = Join-Path ([System.IO.Path]::GetTempPath()) 'PSRafScanArticleDemo'
$resolvedTarget = [System.IO.Path]::GetFullPath($target)
$root = [System.IO.Path]::GetPathRoot($resolvedTarget)
if ([string]::IsNullOrWhiteSpace($resolvedTarget) -or $resolvedTarget -eq $root) {
throw "Refusing to use an empty or root deletion target."
}
Remove-Item -LiteralPath $resolvedTarget -Recurse -Force -WhatIfThe preview revision rescanned with process result 0 and no RecursiveForceDelete finding in both tested Linux runtimes. A controlled WhatIf run reported the preview and left its sentinel file in place. Microsoft documents Remove-Item and its common WhatIf support in the official command reference. -WhatIf is a preview mechanism, not a general guarantee that the surrounding logic is correct. Before any real deletion, inspect how the path is built, what it can resolve to, whether links or wildcards are involved, what permissions apply, and whether the operation belongs in a disposable test environment.
Choose the intended use before you scan
PSRafScan accepts two exact intended-use values:
| Intended use | Choose it when | What the choice means |
|---|---|---|
Ps1File | The source is meant to remain a saved .ps1 file | Apply the checks appropriate to a script file. |
TerminalPaste | The source is meant to be pasted into a live PowerShell terminal | Add the checks that apply only to terminal-paste use. |
At the pinned snapshot, 52 rules apply to both modes and six apply only to TerminalPaste. Choosing a mode does not replace parser analysis, and it does not mean all rules change. If you are unsure, answer one practical question: Where will this exact source unit be used?
For the context differences behind the choice, read PowerShell Script vs. Terminal Paste: Why the Same Code Can Behave Differently and the intended-use documentation.
A seven-step pre-run review workflow
1. Preserve the candidate
Keep an unchanged copy before editing. Record where it came from and its intended purpose. For copied or generated code, remove surrounding prose or Markdown fences into a separate file without silently changing the PowerShell itself.
2. Identify the purpose, inputs, and expected result
Write down what the script is supposed to read, change, create, delete, or call. Identify required privileges, modules, credentials, file paths, environment variables, and network endpoints. Define an observable result that can later be tested.
3. Select Ps1File or TerminalPaste
Choose the context for the source you are actually reviewing. Do not scan as a saved file and then assume the result fully describes a terminal paste.
4. Parse and scan without intentionally executing the target
Run PSRafScan against the existing path or clipboard. For a saved file, an explicit scan can look like this:
RSX -Path ./Candidate.ps1 -IntendedUse Ps1File -FailOn WarningFor exact command options and output destinations, use the run-a-scan documentation. A direct noninteractive scan defaults to a Warning threshold when FailOn is omitted, but an explicit threshold makes the review record clearer.
5. Inspect the issue, location, and suggested fix
Start with parser errors. Then review Error, Warning, and Information findings in context. A finding points to a pattern and a review question; it does not replace understanding the surrounding code.
6. Revise narrowly and rescan
Change only what you can explain. Compare the revision with the preserved copy, reject unrelated changes, and run the same scan again. Continue until the remaining findings are understood and the selected threshold produces the intended process result.
7. Make a separate controlled-test decision
Static acceptance is not permission to run a script on a production system. Decide whether the script needs a disposable directory, test account, isolated host, mocked dependency, nonproduction tenant, preview mode, or explicit human approval. Record the expected result before executing anything.
Interpret result codes without overreading them
The preserved implementation supports these result codes:
| Code | Meaning | Correct interpretation |
|---|---|---|
0 | Pass at the selected FailOn threshold | No finding met the threshold. Lower-severity findings can still be present, and FailOn None always returns a passing threshold result when parsing succeeds. |
1 | Threshold failure | At least one custom-rule finding met or exceeded the selected threshold. Review the report; the code does not mean the target was executed. |
2 | Parser error | Native PowerShell parsing produced an error finding. Correct syntax before relying on subsequent review. |
FailOn accepts Any, Warning, Error, and None. It controls the process result, not which findings appear in the report. Use PlainText or Json according to the review or automation task.
What still requires a controlled runtime test
Even after the selected threshold passes, controlled testing must answer questions that source inspection cannot settle:
- Are required modules and commands available in the target PowerShell session?
- Do file paths and environment-specific values resolve as expected?
- Are authentication, authorization, and elevation requirements satisfied?
- Do external systems return the expected objects and errors?
- Does the script preserve data and state when interrupted or rerun?
- Do prompts, streams, native exit codes, timing, and concurrency behave correctly?
- Does the observed result match the documented purpose?
Use the smallest environment and privileges that can answer those questions. Keep production data and credentials out of the first execution path whenever possible.
Frequently asked questions
Does PSRafScan execute the script it scans?
PSRafScan reads the target and does not intentionally execute it during analysis. That boundary does not make PSRafScan a sandbox, and it does not prove the target is safe or correct. Reports can include matched source text, so review them before sharing.
Is PSRafScan PSScriptAnalyzer or a wrapper around it?
No. PSRafScan has its own rule contract and does not call, wrap, or depend on PSScriptAnalyzer. PSScriptAnalyzer is a separate static checker maintained by the PowerShell Team and community.
Which intended-use mode should I choose?
Use Ps1File for code intended to remain a saved script. Use TerminalPaste for code intended to be pasted into a live terminal. The mode changes which PSRafScan rules are applicable; it does not change the source into another execution form.
Does PSRafScan work on Windows, macOS, and Linux?
The reviewed module manifest and documentation declare Windows, macOS, and Linux targets and require PowerShell 7.4 or later. The owner confirms successful PSRafScan and article testing on Windows, macOS, and Ubuntu. The preserved continuous-integration evidence validates Windows only, and the owner confirmation does not claim behavior in every environment or full cross-platform runtime validation.
Does a passing scan mean the script is safe to run?
No. A pass means no finding met the selected threshold after successful parsing. It does not establish runtime correctness, business intent, environment compatibility, or safety.
Continue the review
See how PSRafScan checks a script
Related content
Then choose the next step that matches your task:
- Run a PSRafScan review
- Choose between
Ps1FileandTerminalPaste - Browse PSRafScan rules
- PowerShell Script vs. Terminal Paste: Why the Same Code Can Behave Differently
- How to Review AI-Generated PowerShell Without Running It
Verification and editorial record
Technical verification: The owner confirms successful PSRafScan and article testing on Windows, macOS, and Ubuntu. This readiness confirmation does not claim behavior in every environment, guarantee runtime correctness or safety, or represent independent human technical review.
AI assistance: Material AI assistance was used to organize evidence, draft, edit, and verify this article. Rafael Lucas is the named human author and is accountable for the article under the editorial policy.
Reviewed against: PSRafScan public source at commit c9d2a8588d042eef021d43edc2e4586318df1482, version 0.2.0-alpha1.
Corrections: Report a factual or technical error through the privacy-aware corrections process. Use the security route for a vulnerability.
Sources
- PSRafScan repository evidence and packaged source at commit
c9d2a8588d042eef021d43edc2e4586318df1482, reviewed July 12, 2026. - Microsoft:
Parser.ParseFile, retrieved July 12, 2026. - Microsoft: PSScriptAnalyzer overview, retrieved July 12, 2026.
- Microsoft:
about_CommonParameters, retrieved July 12, 2026. - Microsoft:
Remove-Item, retrieved July 12, 2026.
