Uh oh!
There was an error while loading. Please reload this page.
Add option to convert from markdown and to convert to DSL - #14
Add option to convert from markdown and to convert to DSL#14AutoSysOps (Leo Visser) (autosysops) wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds Markdown ↔ DSL conversion capabilities to the module by introducing a Markdown-to-object parser and an object-to-Markdown-DSL generator, along with documentation and Pester coverage.
Changes:
- Add
ConvertFrom-MarkdownMarkdownto parse Markdown files into a hierarchical object structure. - Add
ConvertTo-MarkdownDSLto generate an executable DSL scriptblock (or DSL string) from the parsed object structure. - Add extensive Pester tests and README documentation for the new conversion workflows.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 14 comments.
| File | Description |
|---|---|
| tests/Markdown.Tests.ps1 | Adds Pester coverage for parsing Markdown into objects, generating DSL, and executing the DSL. |
| src/functions/public/ConvertFrom-MarkdownMarkdown.ps1 | New parser converting Markdown text into a nested object tree (headers/details/paragraphs/codeblocks/tables). |
| src/functions/public/ConvertTo-MarkdownDSL.ps1 | New generator that emits DSL text/scriptblocks from the parsed object structure. |
| README.md | Documents the new conversion functions and a round-trip workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
AutoSysOps (Leo Visser) (autosysops)
left a comment
There was a problem hiding this comment.
fixed copilot review
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $markDownString += "[PSCustomObject]@{" | ||
| foreach ($property in $item.PSObject.Properties) { | ||
| $markDownString += "'$($property.Name)' = `'$($property.Value)`'; " | ||
| } | ||
| $markDownString += "}$newline" |
There was a problem hiding this comment.
Table row serialization writes '$property.Value' into PowerShell source without escaping embedded single quotes/newlines. A value like O'Reilly will generate invalid DSL, and crafted values could break out of the literal context. Escape table values for single-quoted PowerShell strings (double any ') before writing them into $markDownString.
| [Switch] $DontQuoteString | ||
| ) | ||
| # First the file is converted to a string containing the syntaxt for the markdown file then it will be invoked to be converted by the other functions. |
There was a problem hiding this comment.
Typo in comment: “syntaxt” should be “syntax”.
| # Loop through all childeren of the input object | ||
| foreach ($child in $InputObject) { |
There was a problem hiding this comment.
Typo in comment: “childeren” should be “children”.
| # Check if the word starts with ``` | ||
| if ($word -match '^```') { | ||
| if ($inCodeBlock) { | ||
| Write-Debug "[CodeBlock] End found, returning to parent" | ||
| # Go to the parent | ||
| $currentObject = [ref]$currentObject.Value.Parent | ||
| $inCodeBlock = $false |
There was a problem hiding this comment.
When $inCodeBlock is true, the parser still evaluates table/header/HTML logic for each line before it reaches this fence handler. As a result, code lines beginning with | or # can be mis-parsed as tables/headers instead of being kept as code content. Consider adding an early check near the start of the loop: if $inCodeBlock and the line is not a closing ``` fence, append the raw line to the current CodeBlock content and continue.
| $markDownString += "$($child.Type) " | ||
| if ($child.Level) { $markDownString += "$($child.Level) " } | ||
| if ($child.Title) { $markDownString += "`"$($child.Title)`" " } | ||
| if ($child.Language) { $markDownString += "`"$($child.Language)`" " } | ||
| $markDownString += "{$newline" |
There was a problem hiding this comment.
Title/Language are injected into the generated PowerShell source using double-quoted string literals without escaping. This breaks for values containing "/backticks/newlines and also enables PowerShell expansion ($var, $()) when the generated DSL is executed, which can change content or be abused if the Markdown is untrusted. Use a safe quoting/escaping strategy for PowerShell literals (typically single-quoted strings with ' doubled, or a dedicated escaping helper) before concatenating into $markDownString.
| [OutputType([scriptblock], [string])] | ||
| [CmdletBinding()] | ||
| param( | ||
| [Parameter(Mandatory = $true, Position = 0)] | ||
| [Object[]] $InputObject, | ||
| [Parameter(Mandatory = $false, Position = 1)] | ||
| [Switch] $AsString, | ||
| [Parameter(Mandatory = $false, Position = 2)] | ||
| [Switch] $DontQuoteString |
There was a problem hiding this comment.
[OutputType([scriptblock])] is inaccurate here since -AsString returns a [string]. This can mislead tooling and documentation generation. Consider declaring both output types (string + scriptblock) or adjusting the attribute to reflect the conditional output.
| $testFile = Join-Path ([System.IO.Path]::GetTempPath()) "PesterMarkdownTest_$([guid]::NewGuid().ToString()).md" | ||
| @' |
There was a problem hiding this comment.
These tests rely on $env:TEMP being set. On some PowerShell environments (especially non-Windows), TEMP may be unset or point somewhere unexpected, which can make the tests flaky. Consider using [System.IO.Path]::GetTempPath(), New-TemporaryFile, or Pester’s temp location patterns for portable test files.
| # Create a variable to hold the current working object in the converstion and helper variables | ||
| $currentObject = [ref]$returnObject |
There was a problem hiding this comment.
Typo in comment: “converstion” should be “conversion”.
| # return the right value | ||
| if ($AsString) { | ||
| Write-Debug "[Output] Returning as string" | ||
| return $markDownString | ||
| } | ||
| else { | ||
| Write-Debug "[Output] Returning as scriptblock" | ||
| return [scriptblock]::Create($markDownString) | ||
| } |
There was a problem hiding this comment.
ConvertTo-MarkdownDSL builds a PowerShell DSL script string from InputObject fields (e.g., Title, free‑text content, table cell values) and then passes it directly to [scriptblock]::Create, without escaping quotes or preventing PowerShell interpolation. When InputObject originates from ConvertFrom-MarkdownMarkdown reading a Markdown file, an attacker can craft headings, paragraphs, or table cells containing PowerShell syntax (e.g., $() subexpressions or embedded quotes/semicolons) so that arbitrary commands execute whenever the generated scriptblock is invoked. To mitigate this, treat Markdown‑derived data as pure data by rigorously escaping/encoding all interpolated values (including quotes and $), disabling DontQuoteString for untrusted input, or avoiding scriptblock creation entirely in favor of interpreting the object tree directly.
Description
In response to discussion #8 here is a PR which can read markdown files and generate a object structure out of it which is similar to the ast structure of PowerShell. There is also a function which converts this object structure to the DSL which can than be execute.
Type of change
Checklist