├── .eslintignore ├── .eslintrc.json ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── analyze.yaml ├── .gitignore ├── .markdownlint.json ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json ├── tasks.json └── walkthrough.schema.json ├── .vscodeignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── docs ├── Example.Rule.jsonc ├── example.Rule.ps1 ├── example.Rule.yaml ├── images │ ├── codelens-doc-link.png │ ├── options-schema-flyout.png │ ├── snippet-markdown.png │ ├── snippet-rule-type.png │ └── tasks-provider.png └── ps-rule.yaml ├── extension.md ├── media ├── icon256.png └── walkthroughs │ └── getStarted │ ├── 01_configureOptions.md │ ├── 02_configureSettings.md │ ├── 03_runTasks.md │ ├── 03_runTasks.svg │ ├── 04_learnMore.md │ └── snippets.json ├── package-lock.json ├── package.json ├── pipeline.build.ps1 ├── ps-project.yaml ├── ps-rule.yaml ├── schemas ├── PSRule-language.schema.json ├── PSRule-options.schema.json └── PSRule-resources.schema.json ├── scripts └── schemas.psm1 ├── snippets ├── github-snippets.json ├── json.json ├── markdown.json ├── options.json ├── pipelines-snippets.json ├── powershell.json └── yaml.json ├── src ├── commands │ ├── configureSettings.ts │ ├── createOptionsFile.ts │ ├── createOrEditDocumentation.ts │ ├── openOptionsFile.ts │ ├── runAnalysisTask.ts │ ├── showTasks.ts │ └── walkthroughCopySnippet.ts ├── configuration.ts ├── consts.ts ├── docLens.ts ├── extension.ts ├── logger.ts ├── main.ts ├── powershell.ts ├── tasks.ts ├── test │ ├── runTest.ts │ └── suite │ │ ├── configuration.test.ts │ │ ├── extension.test.ts │ │ ├── index.ts │ │ └── tasks.test.ts └── utils.ts ├── syntaxes ├── comments.json ├── keywords.json ├── rule.json └── yaml-comments.json └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:@typescript-eslint/recommended-requiring-type-checking" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "project": "tsconfig.json", 14 | "sourceType": "module" 15 | }, 16 | "plugins": [ 17 | "@typescript-eslint", 18 | "@typescript-eslint/tslint" 19 | ], 20 | "rules": { 21 | "@typescript-eslint/adjacent-overload-signatures": "warn", 22 | "@typescript-eslint/array-type": "warn", 23 | "@typescript-eslint/ban-types": "warn", 24 | "@typescript-eslint/class-name-casing": "warn", 25 | "@typescript-eslint/consistent-type-assertions": "warn", 26 | "@typescript-eslint/consistent-type-definitions": "warn", 27 | "@typescript-eslint/explicit-member-accessibility": [ 28 | "warn", 29 | { 30 | "accessibility": "explicit" 31 | } 32 | ], 33 | "@typescript-eslint/indent": [ 34 | "warn", 35 | 4, 36 | { 37 | "FunctionDeclaration": { 38 | "parameters": "first" 39 | }, 40 | "FunctionExpression": { 41 | "parameters": "first" 42 | } 43 | } 44 | ], 45 | "@typescript-eslint/interface-name-prefix": "warn", 46 | "@typescript-eslint/member-delimiter-style": [ 47 | "warn", 48 | { 49 | "multiline": { 50 | "delimiter": "semi", 51 | "requireLast": true 52 | }, 53 | "singleline": { 54 | "delimiter": "semi", 55 | "requireLast": false 56 | } 57 | } 58 | ], 59 | "@typescript-eslint/member-ordering": "warn", 60 | "@typescript-eslint/no-empty-function": "warn", 61 | "@typescript-eslint/no-empty-interface": "warn", 62 | "@typescript-eslint/no-explicit-any": "off", 63 | "@typescript-eslint/no-misused-new": "warn", 64 | "@typescript-eslint/no-namespace": "warn", 65 | "@typescript-eslint/no-parameter-properties": "off", 66 | "@typescript-eslint/no-use-before-define": "off", 67 | "@typescript-eslint/no-var-requires": "warn", 68 | "@typescript-eslint/prefer-for-of": "warn", 69 | "@typescript-eslint/prefer-function-type": "warn", 70 | "@typescript-eslint/prefer-namespace-keyword": "warn", 71 | "@typescript-eslint/quotes": [ 72 | "warn", 73 | "double", 74 | { 75 | "avoidEscape": true 76 | } 77 | ], 78 | "@typescript-eslint/semi": [ 79 | "warn", 80 | "always" 81 | ], 82 | "@typescript-eslint/triple-slash-reference": "warn", 83 | "@typescript-eslint/type-annotation-spacing": "warn", 84 | "@typescript-eslint/unified-signatures": "warn", 85 | "arrow-body-style": "warn", 86 | "arrow-parens": [ 87 | "warn", 88 | "as-needed" 89 | ], 90 | "camelcase": "warn", 91 | "comma-dangle": [ 92 | "warn", 93 | "always-multiline" 94 | ], 95 | "complexity": "off", 96 | "constructor-super": "warn", 97 | "curly": "warn", 98 | "dot-notation": "warn", 99 | "eol-last": "warn", 100 | "eqeqeq": [ 101 | "warn", 102 | "smart" 103 | ], 104 | "guard-for-in": "warn", 105 | "id-blacklist": [ 106 | "warn", 107 | "any", 108 | "Number", 109 | "number", 110 | "String", 111 | "string", 112 | "Boolean", 113 | "boolean", 114 | "Undefined", 115 | "undefined" 116 | ], 117 | "id-match": "warn", 118 | "import/order": "warn", 119 | "max-classes-per-file": "off", 120 | "max-len": [ 121 | "warn", 122 | { 123 | "code": 120 124 | } 125 | ], 126 | "new-parens": "warn", 127 | "no-bitwise": "warn", 128 | "no-caller": "warn", 129 | "no-cond-assign": "warn", 130 | "no-console": "warn", 131 | "no-debugger": "warn", 132 | "no-empty": "warn", 133 | "no-eval": "warn", 134 | "no-fallthrough": "off", 135 | "no-invalid-this": "off", 136 | "no-multiple-empty-lines": "warn", 137 | "no-new-wrappers": "warn", 138 | "no-shadow": [ 139 | "warn", 140 | { 141 | "hoist": "all" 142 | } 143 | ], 144 | "no-throw-literal": "warn", 145 | "no-trailing-spaces": "warn", 146 | "no-undef-init": "warn", 147 | "no-underscore-dangle": "warn", 148 | "no-unsafe-finally": "warn", 149 | "no-unused-expressions": "warn", 150 | "no-unused-labels": "warn", 151 | "no-var": "warn", 152 | "object-shorthand": "warn", 153 | "one-var": [ 154 | "warn", 155 | "never" 156 | ], 157 | "prefer-arrow/prefer-arrow-functions": "warn", 158 | "prefer-const": "warn", 159 | "quote-props": [ 160 | "warn", 161 | "consistent-as-needed" 162 | ], 163 | "radix": "warn", 164 | "space-before-function-paren": [ 165 | "warn", 166 | { 167 | "anonymous": "never", 168 | "asyncArrow": "always", 169 | "named": "never" 170 | } 171 | ], 172 | "spaced-comment": "warn", 173 | "use-isnan": "warn", 174 | "valid-typeof": "off", 175 | "@typescript-eslint/tslint/config": [ 176 | "error", 177 | { 178 | "rules": { 179 | "file-header": [ 180 | true, 181 | "Copyright \\(c\\) Microsoft Corporation." 182 | ], 183 | "import-spacing": true, 184 | "jsdoc-format": true, 185 | "no-reference-import": true, 186 | "one-line": [ 187 | true, 188 | "check-catch", 189 | "check-else", 190 | "check-finally", 191 | "check-open-brace", 192 | "check-whitespace" 193 | ], 194 | "whitespace": [ 195 | true, 196 | "check-branch", 197 | "check-decl", 198 | "check-operator", 199 | "check-separator", 200 | "check-type", 201 | "check-typecast" 202 | ] 203 | } 204 | } 205 | ] 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://help.github.com/articles/about-codeowners/ 2 | * @microsoft/psrule 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report errors or an unexpected issue 4 | --- 5 | 6 | **Description of the issue** 7 | 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | 12 | Steps to reproduce the issue: 13 | 14 | ```powershell 15 | 16 | ``` 17 | 18 | **Expected behaviour** 19 | 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Error output** 23 | 24 | Capture any error messages and or terminal output. 25 | 26 | ```text 27 | 28 | ``` 29 | 30 | **Extension version:** 31 | 32 | - Version: **[e.g. 2.6.0]** 33 | 34 | **PSRule module version:** 35 | 36 | Captured output from `Get-InstalledModule PSRule | Format-List Name,Version`: 37 | 38 | ```text 39 | 40 | ``` 41 | 42 | **Additional context** 43 | 44 | Add any other context about the problem here. 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea 4 | --- 5 | 6 | **Is your feature request related to a problem? Please describe.** 7 | 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | 12 | A clear and concise description of what you want to happen. 13 | 14 | **Describe alternatives you've considered** 15 | 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Summary 2 | 3 | 4 | 5 | ## PR Checklist 6 | 7 | - [ ] PR has a meaningful title 8 | - [ ] Summarized changes 9 | - [ ] Change is not breaking 10 | - [ ] This PR is ready to merge and is not **Work in Progress** 11 | - **Code changes** 12 | - [ ] Link to a filed issue 13 | - [ ] [Change log](https://github.com/microsoft/PSRule-vscode/blob/main/CHANGELOG.md) has been updated with change under unreleased section 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Dependabot configuration 3 | # 4 | 5 | # Please see the documentation for all configuration options: 6 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 7 | 8 | version: 2 9 | updates: 10 | # Maintain dependencies for GitHub Actions 11 | - package-ecosystem: 'github-actions' 12 | directory: '/' 13 | schedule: 14 | interval: 'daily' 15 | labels: 16 | - 'ci-quality' 17 | reviewers: 18 | - 'microsoft/psrule' 19 | 20 | # Maintain dependencies for npm 21 | - package-ecosystem: 'npm' 22 | directory: '/' 23 | schedule: 24 | interval: 'daily' 25 | labels: 26 | - 'dependencies' 27 | reviewers: 28 | - 'microsoft/psrule' 29 | -------------------------------------------------------------------------------- /.github/workflows/analyze.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Repository analysis 3 | # 4 | 5 | # NOTES: 6 | # This workflow uses PSRule, CodeQL, and DevSkim. 7 | # You can read more about these linting tools and configuration options here: 8 | # PSRule - https://aka.ms/ps-rule and https://github.com/Microsoft/PSRule.Rules.MSFT.OSS 9 | # CodeQL - https://codeql.github.com/docs/codeql-overview/about-codeql/ 10 | # DevSkim - https://github.com/microsoft/DevSkim-Action and https://github.com/Microsoft/DevSkim 11 | 12 | name: Analyze 13 | on: 14 | push: 15 | branches: [main, 'release/*', 'dependencies/*'] 16 | pull_request: 17 | branches: [main, 'release/*'] 18 | schedule: 19 | - cron: '54 20 * * 0' # At 08:54 PM, on Sunday each week 20 | workflow_dispatch: {} 21 | 22 | permissions: {} 23 | 24 | jobs: 25 | oss: 26 | name: Analyze with PSRule 27 | runs-on: ubuntu-latest 28 | permissions: 29 | contents: read 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 33 | 34 | - name: Run PSRule analysis 35 | uses: microsoft/ps-rule@v2.9.0 36 | with: 37 | modules: PSRule.Rules.MSFT.OSS 38 | prerelease: true 39 | 40 | devskim: 41 | name: Analyze with DevSkim 42 | runs-on: ubuntu-latest 43 | permissions: 44 | actions: read 45 | contents: read 46 | security-events: write 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 50 | 51 | - name: Run DevSkim scanner 52 | uses: microsoft/DevSkim-Action@a6b6966a33b497cd3ae2ebc406edf8f4cc2feec6 # v1.0.15 53 | with: 54 | directory-to-scan: . 55 | 56 | - name: Upload results to security tab 57 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 58 | with: 59 | sarif_file: devskim-results.sarif 60 | 61 | codeql: 62 | name: Analyze with CodeQL 63 | runs-on: ubuntu-latest 64 | permissions: 65 | actions: read 66 | contents: read 67 | security-events: write 68 | steps: 69 | - name: Checkout 70 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 71 | 72 | - name: Initialize CodeQL 73 | uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 74 | with: 75 | languages: 'javascript' 76 | 77 | - name: Autobuild 78 | uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 79 | 80 | - name: Perform CodeQL Analysis 81 | uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | out/ 3 | reports/ 4 | node_modules/ 5 | .vscode-test/ 6 | *.vsix 7 | PSRule/ 8 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "header-increment": true, 4 | "first-header-h1": { 5 | "level": 1 6 | }, 7 | "header-style": { 8 | "style": "atx" 9 | }, 10 | "ul-style": { 11 | "style": "dash" 12 | }, 13 | "list-indent": true, 14 | "ul-start-left": true, 15 | "ul-indent": { 16 | "indent": 2 17 | }, 18 | "no-trailing-spaces": true, 19 | "no-hard-tabs": true, 20 | "no-reversed-links": true, 21 | "no-multiple-blanks": true, 22 | "line-length": { 23 | "line_length": 100, 24 | "code_blocks": false, 25 | "tables": false, 26 | "headers": true 27 | }, 28 | "commands-show-output": true, 29 | "no-missing-space-atx": true, 30 | "no-multiple-space-atx": true, 31 | "no-missing-space-closed-atx": true, 32 | "no-multiple-space-closed-atx": true, 33 | "blanks-around-headers": true, 34 | "header-start-left": true, 35 | "no-duplicate-header": true, 36 | "single-h1": true, 37 | "no-trailing-punctuation": { 38 | "punctuation": ".,;:!" 39 | }, 40 | "no-multiple-space-blockquote": true, 41 | "no-blanks-blockquote": true, 42 | "ol-prefix": { 43 | "style": "one_or_ordered" 44 | }, 45 | "list-marker-space": true, 46 | "blanks-around-fences": true, 47 | "blanks-around-lists": true, 48 | "no-bare-urls": true, 49 | "hr-style": { 50 | "style": "---" 51 | }, 52 | "no-emphasis-as-header": true, 53 | "no-space-in-emphasis": true, 54 | "no-space-in-code": true, 55 | "no-space-in-links": true, 56 | "fenced-code-language": false, 57 | "first-line-h1": false, 58 | "no-empty-links": true, 59 | "proper-names": { 60 | "names": [ 61 | "PowerShell", 62 | "JavaScript" 63 | ], 64 | "code_blocks": false 65 | }, 66 | "no-alt-text": true 67 | } 68 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": false, 4 | "endOfLine": "auto", 5 | "singleQuote": true, 6 | "semi": true, 7 | "bracketSpacing": true, 8 | "printWidth": 100 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-vscode.vscode-typescript-tslint-plugin", 6 | "ms-azure-devops.azure-pipelines", 7 | "ms-devlabs.extension-manifest-editor", 8 | "github.vscode-pull-request-github", 9 | "esbenp.prettier-vscode" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--disable-extension=bewhite.psrule-vscode-preview", 15 | "--disable-extension=bewhite.psrule-vscode", 16 | "--extensionDevelopmentPath=${workspaceFolder}" 17 | ], 18 | "outFiles": [ 19 | "${workspaceFolder}/out/**/*.js" 20 | ], 21 | "preLaunchTask": "Compile" 22 | }, 23 | { 24 | "name": "Extension Tests", 25 | "type": "extensionHost", 26 | "request": "launch", 27 | "presentation": { 28 | "hidden": false, 29 | "group": "", 30 | "order": 1 31 | }, 32 | "internalConsoleOptions": "openOnSessionStart", 33 | "runtimeExecutable": "${execPath}", 34 | "args": [ 35 | "--disable-extension=bewhite.psrule-vscode-preview", 36 | "--disable-extension=bewhite.psrule-vscode", 37 | "--extensionDevelopmentPath=${workspaceFolder}", 38 | "--extensionTestsPath=${workspaceFolder}/out/dist/test/suite/index" 39 | ], 40 | "outFiles": [ 41 | "${workspaceFolder}/out/dist/test/**/*.js" 42 | ], 43 | "preLaunchTask": "Compile" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "node_modules/": true 4 | }, 5 | "search.exclude": { 6 | "out/": true, 7 | "node_modules/": true 8 | }, 9 | "files.associations": { 10 | "**/.azure-pipelines/*.yaml": "azure-pipelines", 11 | "**/.azure-pipelines/jobs/*.yaml": "azure-pipelines" 12 | }, 13 | "json.schemas": [ 14 | { 15 | "url": ".vscode/walkthrough.schema.json", 16 | "fileMatch": [ 17 | "**/media/walkthroughs/**/snippets.json" 18 | ] 19 | } 20 | ], 21 | "yaml.format.singleQuote": true, 22 | "files.insertFinalNewline": true, 23 | "editor.insertSpaces": true, 24 | "editor.formatOnSave": true, 25 | "editor.tabSize": 2, 26 | "editor.detectIndentation": false, 27 | "[json]": { 28 | "editor.defaultFormatter": "vscode.json-language-features" 29 | }, 30 | "[typescript]": { 31 | //"editor.defaultFormatter": "esbenp.prettier-vscode", 32 | "editor.tabSize": 4 33 | }, 34 | "[powershell]": { 35 | "editor.tabSize": 4, 36 | "editor.formatOnSave": false 37 | }, 38 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 39 | "typescript.tsc.autoDetect": "off", 40 | "cSpell.words": [ 41 | "Pseudoterminal", 42 | "pwsh" 43 | ], 44 | "git.branchProtection": [ 45 | "main", 46 | "release/*" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build: dev", 8 | "detail": "Locally build the extension as the 'dev' channel.", 9 | "type": "shell", 10 | "command": "Invoke-Build Build -Channel dev", 11 | "group": "build", 12 | "presentation": { 13 | "focus": false, 14 | "panel": "dedicated", 15 | "clear": true 16 | }, 17 | "problemMatcher": [], 18 | "osx": { 19 | "options": { 20 | "shell": { 21 | "executable": "pwsh", 22 | "args": [ 23 | "-c" 24 | ] 25 | } 26 | } 27 | }, 28 | "linux": { 29 | "options": { 30 | "shell": { 31 | "executable": "pwsh", 32 | "args": [ 33 | "-c" 34 | ] 35 | } 36 | } 37 | } 38 | }, 39 | { 40 | "label": "Install: preview", 41 | "detail": "Build and install the extension as the 'preview' channel.", 42 | "type": "shell", 43 | "command": "Invoke-Build Install", 44 | "presentation": { 45 | "focus": false, 46 | "panel": "dedicated", 47 | "clear": true 48 | }, 49 | "problemMatcher": [], 50 | "osx": { 51 | "options": { 52 | "shell": { 53 | "executable": "pwsh", 54 | "args": [ 55 | "-c" 56 | ] 57 | } 58 | } 59 | }, 60 | "linux": { 61 | "options": { 62 | "shell": { 63 | "executable": "pwsh", 64 | "args": [ 65 | "-c" 66 | ] 67 | } 68 | } 69 | } 70 | }, 71 | { 72 | "label": "Install: stable", 73 | "detail": "Build and install the extension as the 'stable' channel.", 74 | "type": "shell", 75 | "command": "Invoke-Build Install -Channel stable", 76 | "presentation": { 77 | "focus": false, 78 | "panel": "dedicated", 79 | "clear": true 80 | }, 81 | "problemMatcher": [], 82 | "osx": { 83 | "options": { 84 | "shell": { 85 | "executable": "pwsh", 86 | "args": [ 87 | "-c" 88 | ] 89 | } 90 | } 91 | }, 92 | "linux": { 93 | "options": { 94 | "shell": { 95 | "executable": "pwsh", 96 | "args": [ 97 | "-c" 98 | ] 99 | } 100 | } 101 | } 102 | }, 103 | { 104 | "label": "Install: dev", 105 | "detail": "Build and install the extension as the 'dev' channel.", 106 | "type": "shell", 107 | "command": "Invoke-Build Install -Channel dev", 108 | "presentation": { 109 | "focus": false, 110 | "panel": "dedicated", 111 | "clear": true 112 | }, 113 | "problemMatcher": [], 114 | "osx": { 115 | "options": { 116 | "shell": { 117 | "executable": "pwsh", 118 | "args": [ 119 | "-c" 120 | ] 121 | } 122 | } 123 | }, 124 | "linux": { 125 | "options": { 126 | "shell": { 127 | "executable": "pwsh", 128 | "args": [ 129 | "-c" 130 | ] 131 | } 132 | } 133 | } 134 | }, 135 | { 136 | "label": "Clean", 137 | "detail": "Remove temporary working files and directories.", 138 | "type": "shell", 139 | "command": "Invoke-Build Clean", 140 | "presentation": { 141 | "reveal": "never", 142 | "focus": false, 143 | "panel": "dedicated", 144 | "clear": true 145 | }, 146 | "problemMatcher": [], 147 | "osx": { 148 | "options": { 149 | "shell": { 150 | "executable": "pwsh", 151 | "args": [ 152 | "-c" 153 | ] 154 | } 155 | } 156 | }, 157 | "linux": { 158 | "options": { 159 | "shell": { 160 | "executable": "pwsh", 161 | "args": [ 162 | "-c" 163 | ] 164 | } 165 | } 166 | } 167 | }, 168 | { 169 | "label": "Compile", 170 | "detail": "Run TypeScript compile.", 171 | "type": "npm", 172 | "script": "compile", 173 | "presentation": { 174 | "focus": false, 175 | "panel": "dedicated", 176 | "clear": true 177 | }, 178 | "problemMatcher": [ 179 | "$tsc" 180 | ] 181 | }, 182 | { 183 | "label": "Analyze repository", 184 | "detail": "Run analysis on files within the repository.", 185 | "type": "shell", 186 | "command": "Invoke-Build Rules -AssertStyle Client", 187 | "group": "build", 188 | "presentation": { 189 | "focus": false, 190 | "panel": "dedicated", 191 | "clear": true 192 | }, 193 | "problemMatcher": [], 194 | "osx": { 195 | "options": { 196 | "shell": { 197 | "executable": "pwsh", 198 | "args": [ 199 | "-c" 200 | ] 201 | } 202 | } 203 | }, 204 | "linux": { 205 | "options": { 206 | "shell": { 207 | "executable": "pwsh", 208 | "args": [ 209 | "-c" 210 | ] 211 | } 212 | } 213 | } 214 | }, 215 | { 216 | "type": "npm", 217 | "detail": "Run TypeScript compile and watch for changes.", 218 | "script": "watch", 219 | "problemMatcher": "$tsc-watch", 220 | "isBackground": true, 221 | "presentation": { 222 | "reveal": "never" 223 | }, 224 | "group": { 225 | "kind": "build", 226 | "isDefault": true 227 | } 228 | }, 229 | { 230 | "label": "Compile", 231 | "type": "npm", 232 | "script": "compile", 233 | "presentation": { 234 | "focus": false, 235 | "panel": "dedicated", 236 | "clear": true 237 | }, 238 | "problemMatcher": [ 239 | "$tsc" 240 | ] 241 | }, 242 | { 243 | "type": "PSRule", 244 | "problemMatcher": [ 245 | "$PSRule" 246 | ], 247 | "outcome": [ 248 | "Fail", 249 | "Error", 250 | "Pass" 251 | ], 252 | "label": "PSRule: Run analysis", 253 | "detail": "Run analysis on files within the repository.", 254 | "presentation": { 255 | "focus": false, 256 | "panel": "dedicated", 257 | "clear": true 258 | } 259 | } 260 | ] 261 | } 262 | -------------------------------------------------------------------------------- /.vscode/walkthrough.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft-07/schema#", 3 | "type": "array", 4 | "items": { 5 | "$ref": "#/definitions/snippet" 6 | }, 7 | "definitions": { 8 | "snippet": { 9 | "type": "object", 10 | "properties": { 11 | "name": { 12 | "type": "string" 13 | }, 14 | "snippet": { 15 | "type": "array", 16 | "items": { 17 | "type": "string" 18 | } 19 | } 20 | }, 21 | "additionalProperties": false, 22 | "required": [ 23 | "name", 24 | "snippet" 25 | ], 26 | "defaultSnippets": [ 27 | { 28 | "label": "New snippet", 29 | "body": { 30 | "name": "", 31 | "snippet": "" 32 | } 33 | } 34 | ] 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .azure-pipelines/ 2 | .github/ 3 | .vscode/ 4 | docs/ 5 | out/package/ 6 | out/dist/test/ 7 | src/ 8 | scripts/ 9 | node_modules/ 10 | reports/ 11 | .ps-rule/ 12 | pipeline.build.ps1 13 | .gitignore 14 | tsconfig.json 15 | tslint.json 16 | **/*.map 17 | *.ts 18 | .eslintignore 19 | ps-rule.yaml 20 | ps-project.yaml 21 | SECURITY.md 22 | SUPPORT.md 23 | .markdownlint.json 24 | out/updates.txt 25 | README.md 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | All notable changes to this extension will be documented in this file. 4 | This extension is available in two release channels for Visual Studio Code from the Visual Studio Marketplace. 5 | We recommend only installing one channel for the best experience. 6 | Installing both channels may cause unexpected behavior. 7 | 8 | - [Preview][ext-preview] - More frequent releases but more likely to contain bugs. 9 | - _Preview_ is where updates are available before they released to _Stable_. 10 | - This channel includes changes listed in the _Unreleased_ heading. 11 | - Versioning for _Preview_ follows an `year.month.revision` that increments for each release. 12 | - [Stable][ext-stable] - Less frequent releases, with more user testing, experimental features are disabled. 13 | - Uses [semantic versioning](https://semver.org/) to declare changes. 14 | - Aligned to versioning of PSRule. 15 | 16 | Continue reading to see the changes included in the latest version. 17 | 18 | ## Unreleased 19 | 20 | ## v2.9.1 21 | 22 | What's changed since v2.9.0: 23 | 24 | - Engineering: 25 | - Updated display name to clearly indicate this extension is V2 in the marketplace by @BernieWhite. 26 | 27 | ## v2.9.0 28 | 29 | What's changed since v2.8.0: 30 | 31 | - New features: 32 | - Getting started walkthrough in Visual Studio Code is now generally available by @BernieWhite 33 | [#1099](https://github.com/microsoft/PSRule-vscode/issues/1099) 34 | - CodeLens for open or editing documentation for a rule is now generally available by @BernieWhite 35 | [#1139](https://github.com/microsoft/PSRule-vscode/issues/1139) 36 | - Added configuration to tune logging for excluded or suppressed rules by @BernieWhite. 37 | [#1068](https://github.com/microsoft/PSRule-vscode/issues/1068) 38 | - Use these options to reduce output noise when testing in Visual Studio Code. 39 | - The `PSRule.execution.ruleExcluded` setting configures excluded rules. 40 | - The `PSRule.execution.ruleSuppressed` setting configures suppressed rules. 41 | - Add running analysis to getting started walkthrough by @BernieWhite. 42 | [#1093](https://github.com/microsoft/PSRule-vscode/issues/1093) 43 | - **Important:** Added support for new unprocessed object option by @BernieWhite. 44 | [#1127](https://github.com/microsoft/PSRule-vscode/issues/1127) 45 | - The `PSRule.execution.unprocessedObject` setting configures what happens when objects are not processed. 46 | - The `PSRule.execution.notProcessedWarning` setting has been deprecated inline with PSRule support. 47 | - For more information see [deprecations](https://aka.ms/ps-rule/deprecations#execution-options). 48 | - Engineering: 49 | - Updated PSRule schema files. 50 | [#1092](https://github.com/microsoft/PSRule-vscode/pull/1092) 51 | [#1110](https://github.com/microsoft/PSRule-vscode/pull/1110) 52 | [#1125](https://github.com/microsoft/PSRule-vscode/pull/1125) 53 | [#1156](https://github.com/microsoft/PSRule-vscode/pull/1156) 54 | - Bump vscode engine to v1.78.1. 55 | [#1146](https://github.com/microsoft/PSRule-vscode/pull/1146) 56 | - Bump typescript to v5.1.3. 57 | [#1152](https://github.com/microsoft/PSRule-vscode/pull/1152) 58 | - Bump and rename vsce package to `@vscode/vsce` v2.19.0 by @BernieWhite. 59 | [#1090](https://github.com/microsoft/PSRule-vscode/issues/1090) 60 | - Bump @vscode/test-electron to v2.3.2 61 | [#1124](https://github.com/microsoft/PSRule-vscode/pull/1124) 62 | 63 | ## v2.8.0 64 | 65 | What's changed since v2.7.0: 66 | 67 | - Engineering: 68 | - Updated PSRule schema files. 69 | [#1060](https://github.com/microsoft/PSRule-vscode/pull/1060) 70 | [#1062](https://github.com/microsoft/PSRule-vscode/pull/1062) 71 | - Bump vscode engine to v1.76.0. 72 | [#1036](https://github.com/microsoft/PSRule-vscode/pull/1036) 73 | - Bump mocha to v10.2.0. 74 | [#957](https://github.com/microsoft/PSRule-vscode/pull/957) 75 | - Bump fs-extra to v11.1.1. 76 | [#1058](https://github.com/microsoft/PSRule-vscode/pull/1058) 77 | - Bump glob to v8.1.0. 78 | [#990](https://github.com/microsoft/PSRule-vscode/pull/990) 79 | - Bump typescript to v5.0.2. 80 | [#1053](https://github.com/microsoft/PSRule-vscode/pull/1053) 81 | - Bump minimist to v1.2.8. 82 | [#1014](https://github.com/microsoft/PSRule-vscode/pull/1014) 83 | - Bump vscode-languageclient to v8.1.0. 84 | [#1021](https://github.com/microsoft/PSRule-vscode/pull/1021) 85 | - Bump @vscode/test-electron to v2.3.0. 86 | [#1035](https://github.com/microsoft/PSRule-vscode/pull/1035) 87 | 88 | ## v2.7.0 89 | 90 | What's changed since v2.6.0: 91 | 92 | - General improvement: 93 | - Update snippet versions and links by @BernieWhite 94 | [#955](https://github.com/microsoft/PSRule-vscode/issues/955) 95 | - Engineering: 96 | - Updated PSRule schema files. 97 | [#933](https://github.com/microsoft/PSRule-vscode/pull/933) 98 | - Bump vscode engine to v1.74.0. 99 | [#952](https://github.com/microsoft/PSRule-vscode/pull/952) 100 | - Bump fs-extra to v11.1.0. 101 | [#939](https://github.com/microsoft/PSRule-vscode/pull/939) 102 | - Bump vsce to v2.15.0. 103 | [#943](https://github.com/microsoft/PSRule-vscode/pull/943) 104 | - Bump typescript to v4.9.4. 105 | [#954](https://github.com/microsoft/PSRule-vscode/pull/954) 106 | - Bump @vscode/test-electron to v2.2.1. 107 | [#946](https://github.com/microsoft/PSRule-vscode/pull/946) 108 | - Bug fixes: 109 | - Fixed extension badge URL by @BernieWhite. 110 | [#981](https://github.com/microsoft/PSRule-vscode/issues/981) 111 | 112 | ## v2.6.0 113 | 114 | What's changed since v2.5.0: 115 | 116 | - Engineering: 117 | - Updated PSRule schema files. 118 | [#920](https://github.com/microsoft/PSRule-vscode/pull/920) 119 | - Bump vscode engine to v1.73.1. 120 | [#922](https://github.com/microsoft/PSRule-vscode/pull/922) 121 | - Bump vsce to v2.14.0. 122 | [#916](https://github.com/microsoft/PSRule-vscode/pull/916) 123 | - Bump @vscode/test-electron to v2.2.0. 124 | [#902](https://github.com/microsoft/PSRule-vscode/pull/902) 125 | - Bump typescript to v4.9.3. 126 | [#925](https://github.com/microsoft/PSRule-vscode/pull/925) 127 | 128 | ## v2.5.0 129 | 130 | What's changed since v2.4.0: 131 | 132 | - General improvements: 133 | - Added starter pipeline snippet for Azure Pipelines by @BernieWhite. 134 | [#851](https://github.com/microsoft/PSRule-vscode/issues/851) 135 | - Added a warning when multiple channels are installed by @BernieWhite. 136 | [#870](https://github.com/microsoft/PSRule-vscode/issues/870) 137 | - Installing both the _Preview_ and _Stable_ channels is not supported and may cause issues. 138 | - Engineering: 139 | - Updated PSRule schema files. 140 | [#844](https://github.com/microsoft/PSRule-vscode/pull/844) 141 | [#863](https://github.com/microsoft/PSRule-vscode/pull/863) 142 | - Bump vscode engine to v1.71.0. 143 | [#843](https://github.com/microsoft/PSRule-vscode/pull/843) 144 | - Bump typescript to v4.8.4. 145 | [#873](https://github.com/microsoft/PSRule-vscode/pull/873) 146 | 147 | ## v2.4.0 148 | 149 | What's changed since v2.3.0: 150 | 151 | - General improvements: 152 | - **Experimental:** Added a walkthrough for getting started with PSRule by @BernieWhite. 153 | [#771](https://github.com/microsoft/PSRule-vscode/issues/771) 154 | - Added steps for basic configuration and documentation. 155 | - To use try this feature, install the preview channel with experimental features enabled. 156 | - Engineering: 157 | - Updated PSRule schema files. 158 | [#812](https://github.com/microsoft/PSRule-vscode/pull/812) 159 | - Bump vscode engine to v1.70.0. 160 | [#800](https://github.com/microsoft/PSRule-vscode/pull/800) 161 | - Bump vsce to v2.10.2. 162 | [#821](https://github.com/microsoft/PSRule-vscode/pull/821) 163 | - Bump typescript to v4.8.2. 164 | [#830](https://github.com/microsoft/PSRule-vscode/pull/830) 165 | 166 | ## v2.3.0 167 | 168 | What's changed since v2.2.0: 169 | 170 | - General improvements: 171 | - Added configuration option for baseline by @BernieWhite. 172 | [#770](https://github.com/microsoft/PSRule-vscode/issues/770) 173 | - Configure the default baseline in the extension settings. 174 | - Baseline can be overridden using the `baseline` property on a PSRule task. 175 | - Engineering: 176 | - Updated PSRule schema files. 177 | [#767](https://github.com/microsoft/PSRule-vscode/pull/767) 178 | - Bump vscode-languageclient v8.0.2. 179 | [#777](https://github.com/microsoft/PSRule-vscode/pull/777) 180 | - Bum vscode engine to v1.69.1. 181 | [#797](https://github.com/microsoft/PSRule-vscode/pull/797) 182 | 183 | ## v2.2.0 184 | 185 | What's changed since v2.1.0: 186 | 187 | - General improvements: 188 | - Added command to create options file by @BernieWhite. 189 | [#662](https://github.com/microsoft/PSRule-vscode/issues/662) 190 | - From the command palette choose _PSRule: Create options file_. 191 | - Engineering: 192 | - Bump vscode engine to v1.68.1. 193 | [#753](https://github.com/microsoft/PSRule-vscode/pull/753) 194 | - Bump vscode-languageclient from v8.0.1. 195 | [#703](https://github.com/microsoft/PSRule-vscode/pull/703) 196 | - Updated PSRule schema files. 197 | [#743](https://github.com/microsoft/PSRule-vscode/pull/743) 198 | 199 | ## v2.1.0 200 | 201 | What's changed since v2.0.0: 202 | 203 | - New features: 204 | - **Experimental:** CodeLens provides links to open or create rule documentation by @BernieWhite. 205 | [#227](https://github.com/microsoft/PSRule-vscode/issues/227) 206 | - Link from rules allows markdown documentation to be created or edited. 207 | - When existing markdown documentation exists, file is opened in editor. 208 | - When documentation for a rule does not exist, a new file is created from a snippet. 209 | - Added settings to configure the location for storing documentation, 210 | and the snippet used to create documentation. 211 | - To use try this feature, install the preview channel with experimental features enabled. 212 | - Engineering: 213 | - Bump fs-extra to v10.1.0. 214 | [#670](https://github.com/microsoft/PSRule-vscode/pull/670) 215 | - Updated PSRule schema files. 216 | [#688](https://github.com/microsoft/PSRule-vscode/pull/688) 217 | 218 | ## v2.0.0 219 | 220 | What's changed since v1.7.0: 221 | 222 | - PSRule v2.0.0 support: 223 | - Added resource snippets for Suppression Groups. 224 | [#588](https://github.com/microsoft/PSRule-vscode/issues/588) 225 | - Updated schema to support Suppression Groups. 226 | [#574](https://github.com/microsoft/PSRule-vscode/issues/574) 227 | - Updated schema to support convert and case sensitive properties. 228 | [#626](https://github.com/microsoft/PSRule-vscode/pull/626) 229 | [#630](https://github.com/microsoft/PSRule-vscode/pull/630) 230 | - Updated schema to support improved validation of resource names. 231 | [#638](https://github.com/microsoft/PSRule-vscode/pull/638) 232 | - General improvements: 233 | - Added resource snippets for JSON and JSONC files. 234 | [#477](https://github.com/microsoft/PSRule-vscode/issues/477) 235 | - Improved compatibility with PowerShell extension. 236 | [#607](https://github.com/microsoft/PSRule-vscode/issues/607) 237 | - Supports either PowerShell or PowerShell Preview installed for running tasks. 238 | - If PowerShell extension is not installed or enabled, basic functions are still available. 239 | - Engineering: 240 | - Added workflow to sync PSRule schemas. 241 | [#592](https://github.com/microsoft/PSRule-vscode/issues/592) 242 | - Bump vscode engine to v1.66.0. 243 | [#653](https://github.com/microsoft/PSRule-vscode/pull/653) 244 | - Bug fixes: 245 | - Fixed change syntax highlighting on PowerShell files. 246 | [#495](https://github.com/microsoft/PSRule-vscode/issues/495) 247 | 248 | ## v1.7.0 249 | 250 | What's changed since v1.6.0: 251 | 252 | - General improvements: 253 | - Added `version` language expression. [#539](https://github.com/microsoft/PSRule-vscode/issues/539) 254 | - Added `hasDefault` language expression. [#540](https://github.com/microsoft/PSRule-vscode/issues/540) 255 | - Engineering: 256 | - Bump vscode engine to v1.64.0. [#586](https://github.com/microsoft/PSRule-vscode/pull/586) 257 | 258 | ## v1.6.0 259 | 260 | What's changed since v1.5.0: 261 | 262 | - General improvements: 263 | - Added `hasSchema` language expression. [#520](https://github.com/microsoft/PSRule-vscode/issues/520) 264 | - Engineering: 265 | - Bump vscode engine to v1.63.0. [#508](https://github.com/microsoft/PSRule-vscode/pull/508) 266 | 267 | ## v1.5.0 268 | 269 | What's changed since v1.4.0: 270 | 271 | - General improvements: 272 | - Added `Rule.Baseline` option configuration the default baseline with module configuration. 273 | [#475](https://github.com/microsoft/PSRule-vscode/issues/475) 274 | - Added support for resources within `.Rule.json`, `.Rule.jsonc`, and `.Rule.yml` files. 275 | [#476](https://github.com/microsoft/PSRule-vscode/issues/476) 276 | - Configured workspace trust. 277 | [#304](https://github.com/microsoft/PSRule-vscode/issues/304) 278 | - Currently the extension relies on PowerShell which only works when the workspace is trusted. 279 | - Engineering: 280 | - Bump vscode engine to v1.62.0. 281 | [#473](https://github.com/microsoft/PSRule-vscode/pull/473) 282 | 283 | ## v1.4.0 284 | 285 | What's changed since v1.3.0: 286 | 287 | - General improvements: 288 | - Added options schema to support additional options. 289 | [#395](https://github.com/microsoft/PSRule-vscode/issues/395) 290 | [#451](https://github.com/microsoft/PSRule-vscode/issues/451) 291 | - Added support for `Input.IgnoreRepositoryCommon`, `Output.Footer`, `Output.JsonIndent`, and `Rule.IncludeLocal`. 292 | - Added expressions improvements: 293 | [#452](https://github.com/microsoft/PSRule-vscode/issues/452) 294 | - Added `SetOf`, `Subset`, and `Count` set conditions. 295 | - Added `name`, and `type` properties to `Expression` objects. 296 | - Engineering: 297 | - Bump vscode engine to v1.61.0. 298 | [#432](https://github.com/microsoft/PSRule-vscode/pull/432) 299 | 300 | ## v1.3.0 301 | 302 | What's changed since v1.2.0: 303 | 304 | - General improvements: 305 | - Added YAML Rule support to language schema. [#361](https://github.com/microsoft/PSRule-vscode/issues/361) 306 | - Added starter snippet for GitHub Actions workflow. [#362](https://github.com/microsoft/PSRule-vscode/issues/362) 307 | - Updated options schema to support `include` option within PSRule v1.6.0. [#363](https://github.com/microsoft/PSRule-vscode/issues/363) 308 | - Engineering: 309 | - Bump vscode engine to v1.59.0. [#358](https://github.com/microsoft/PSRule-vscode/pull/358) 310 | 311 | ## v1.2.0 312 | 313 | What's changed since v1.1.0: 314 | 315 | - General improvements: 316 | - Updated extension to latest icon. [#356](https://github.com/microsoft/PSRule-vscode/issues/356) 317 | - Added string selector support to language schema. [#327](https://github.com/microsoft/PSRule-vscode/issues/327) 318 | - Updated options schema to support PSRule v1.5.0. [#328](https://github.com/microsoft/PSRule-vscode/issues/328) 319 | - Engineering: 320 | - Bump vscode engine to v1.58.1. [#325](https://github.com/microsoft/PSRule-vscode/pull/325) 321 | 322 | ## v1.1.0 323 | 324 | What's changed since v1.0.0: 325 | 326 | - New features: 327 | - Added `PSRule: Run analysis` quick task to call `Assert-PSRule` for the current workspace. [#226](https://github.com/microsoft/PSRule-vscode/issues/226) 328 | - To configure set `path`, `inputPath`, `baseline`, `module`, and `outcome` per task. 329 | - The default task will run analysis in the current workspace using rules in `.ps-rule/`. 330 | - Requires v1.4.0 or greater of PSRule PowerShell module installed. 331 | - Added `$PSRule` problem matcher for analysis tasks. [#234](https://github.com/microsoft/PSRule-vscode/issues/234) 332 | - Source locations for rules failures are detected when using the `VisualStudioCode` style. 333 | - General improvements: 334 | - Preview channel will notify that a stable version is available. [#235](https://github.com/microsoft/PSRule-vscode/issues/235) 335 | - Added PSRule options schema support updates. 336 | - Added `Output.Banner` option. [#264](https://github.com/microsoft/PSRule-vscode/issues/264) 337 | - Improved validation for the requires option. [#265](https://github.com/microsoft/PSRule-vscode/issues/265) 338 | - Added support for new style options `VisualStudioCode` and `Detect`. [#266](https://github.com/microsoft/PSRule-vscode/issues/266) 339 | - Engineering: 340 | - Bump vscode engine to v1.56.0. [#241](https://github.com/microsoft/PSRule-vscode/pull/241) 341 | 342 | ## v1.0.0 343 | 344 | What's changed since v0.18.0: 345 | 346 | - General improvements: 347 | - Added support for `input.ignoreGitPath` option. [#231](https://github.com/microsoft/PSRule-vscode/issues/231) 348 | - Added feature documentation. [#151](https://github.com/microsoft/PSRule-vscode/issues/151) 349 | - Engineering: 350 | - Split extension into two release channels, _Preview_ and _Stable_. [#150](https://github.com/microsoft/PSRule-vscode/issues/150) 351 | - Preview channel appears as _PSRule (Preview)_, with stable appearing as _PSRule_. [#229](https://github.com/microsoft/PSRule-vscode/issues/229) 352 | - Improved extension performance and size by bundling. [#222](https://github.com/microsoft/PSRule-vscode/issues/222) 353 | 354 | ## v0.18.0 355 | 356 | What's changed since v0.17.0: 357 | 358 | - General improvements: 359 | - Added support for configuring conventions. [#199](https://github.com/Microsoft/PSRule-vscode/issues/199) 360 | - Added support for selectors. [#206](https://github.com/Microsoft/PSRule-vscode/issues/206) 361 | - Updated options and language schema to support `binding.preferTargetInfo` option. [#207](https://github.com/Microsoft/PSRule-vscode/issues/207) 362 | - Updated language schema to add `apiVersion` property. [#208](https://github.com/Microsoft/PSRule-vscode/issues/208) 363 | - Engineering: 364 | - Bump vscode engine to v1.55.0. [#204](https://github.com/microsoft/PSRule-vscode/pull/204) 365 | 366 | ## v0.17.0 367 | 368 | What's changed since v0.16.0: 369 | 370 | - General improvements: 371 | - Migrate repository to Microsoft GitHub org. [#152](https://github.com/Microsoft/PSRule-vscode/issues/152) 372 | - Engineering: 373 | - Bump vscode engine to v1.52.0. 374 | 375 | ## v0.16.0 376 | 377 | What's changed since v0.15.0: 378 | 379 | - General improvements: 380 | - Updated options schema to v0.21.0. [#135](https://github.com/Microsoft/PSRule-vscode/issues/135) 381 | - Updated language schema to v0.21.0. [#134](https://github.com/Microsoft/PSRule-vscode/issues/134) 382 | - Engineering: 383 | - Bump vscode engine to v1.50.0. 384 | 385 | ## v0.15.0 386 | 387 | What's changed since v0.14.0: 388 | 389 | - General improvements: 390 | - Updated options schema to v0.20.0. [#106](https://github.com/Microsoft/PSRule-vscode/issues/106) 391 | - Updated language schema to v0.20.0. [#107](https://github.com/Microsoft/PSRule-vscode/issues/107) 392 | - Engineering: 393 | - Bump vscode engine to v1.49.0. 394 | 395 | ## v0.14.0 396 | 397 | What's changed since v0.13.0: 398 | 399 | - General improvements: 400 | - Updated options schema to v0.19.0. [#87](https://github.com/Microsoft/PSRule-vscode/issues/87) 401 | 402 | ## v0.13.0 403 | 404 | What's changed since v0.12.0: 405 | 406 | - New features: 407 | - Added snippet for ModuleConfig resource. [#75](https://github.com/Microsoft/PSRule-vscode/issues/75) 408 | - General improvements: 409 | - Updated language schema to v0.17.0. [#73](https://github.com/Microsoft/PSRule-vscode/issues/73) 410 | 411 | ## v0.12.0 412 | 413 | What's changed since v0.11.0: 414 | 415 | - General improvements: 416 | - Updated options schema to v0.16.0. [#68](https://github.com/Microsoft/PSRule-vscode/issues/68) 417 | 418 | ## v0.11.0 419 | 420 | What's changed since v0.10.0: 421 | 422 | - General improvements: 423 | - Updated options schema to v0.14.0. [#63](https://github.com/Microsoft/PSRule-vscode/issues/63) 424 | 425 | ## v0.10.0 426 | 427 | What's changed since v0.9.0: 428 | 429 | - General improvements: 430 | - Updated markdown snippet to include links section and online version. [#60](https://github.com/Microsoft/PSRule-vscode/issues/60) 431 | - Updated options schema to v0.13.0. [#59](https://github.com/Microsoft/PSRule-vscode/issues/59) 432 | 433 | ## v0.9.0 434 | 435 | What's changed since v0.8.0: 436 | 437 | - General improvements: 438 | - Updated schemas to v0.12.0. [#54](https://github.com/Microsoft/PSRule-vscode/issues/54) 439 | 440 | ## v0.8.0 441 | 442 | What's changed since v0.7.0: 443 | 444 | - General improvements: 445 | - Updated options schema to v0.11.0. [#49](https://github.com/Microsoft/PSRule-vscode/issues/49) 446 | 447 | ## v0.7.0 448 | 449 | What's changed since v0.6.0: 450 | 451 | - General improvements: 452 | - Updated options schema to v0.10.0. [#44](https://github.com/Microsoft/PSRule-vscode/issues/44) 453 | 454 | ## v0.6.0 455 | 456 | What's changed since v0.5.0: 457 | 458 | - New features: 459 | - Added language schema. [#39](https://github.com/Microsoft/PSRule-vscode/issues/39) 460 | - Added snippet for baseline resource. [#40](https://github.com/Microsoft/PSRule-vscode/issues/40) 461 | - Added highlighting for `Synopsis:` resource comments. [#41](https://github.com/Microsoft/PSRule-vscode/issues/41) 462 | - General improvements: 463 | - Updated options schema to v0.9.0. [#38](https://github.com/Microsoft/PSRule-vscode/issues/38) 464 | 465 | ## v0.5.0 466 | 467 | What's changed since v0.4.0: 468 | 469 | - New features: 470 | - Added snippet and syntax support for Reason keyword. [#32](https://github.com/Microsoft/PSRule-vscode/issues/32) 471 | - General improvements: 472 | - Updated options schema to v0.8.0. [#31](https://github.com/Microsoft/PSRule-vscode/issues/31) 473 | 474 | ## v0.4.0 475 | 476 | What's changed since v0.3.0: 477 | 478 | - General improvements: 479 | - Updated options schema to v0.7.0. [#26](https://github.com/Microsoft/PSRule-vscode/issues/26) 480 | 481 | ## v0.3.0 482 | 483 | What's changed since v0.2.0: 484 | 485 | - New features: 486 | - Added highlighting for `Synopsis:` metadata in comments. [#16](https://github.com/Microsoft/PSRule-vscode/issues/16) 487 | - Added syntax highlighting and snippet for `Recommend` keyword. [#17](https://github.com/Microsoft/PSRule-vscode/issues/17) 488 | - Added markdown snippet for rule documentation. [#19](https://github.com/Microsoft/PSRule-vscode/issues/19) 489 | 490 | ## v0.2.0 491 | 492 | What's changed since v0.1.0: 493 | 494 | - General improvements: 495 | - Updated options schema to v0.5.0. [#12](https://github.com/Microsoft/PSRule-vscode/issues/12) 496 | - Bug fixes: 497 | - Fixed CI badge not displaying in VSCode extension tab. [#8](https://github.com/Microsoft/PSRule-vscode/issues/8) 498 | - Fixed syntax highlighting for keywords that are included in comments. [#10](https://github.com/Microsoft/PSRule-vscode/issues/10) 499 | 500 | ## v0.1.0 501 | 502 | - Initial release. 503 | 504 | [ext-preview]: https://marketplace.visualstudio.com/items?itemName=bewhite.psrule-vscode-preview 505 | [ext-stable]: https://marketplace.visualstudio.com/items?itemName=bewhite.psrule-vscode 506 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PSRule 2 | 3 | Welcome, and thank you for your interest in contributing to PSRule! 4 | 5 | There are many ways in which you can contribute, beyond writing code. 6 | The goal of this document is to provide a high-level overview of how you can get involved. 7 | 8 | - [Reporting issues](#reporting-issues) 9 | - Fix bugs or add features 10 | 11 | ## Contributor License Agreement (CLA) 12 | 13 | This project welcomes contributions and suggestions. Most contributions require you to 14 | agree to a Contributor License Agreement (CLA) declaring that you have the right to, 15 | and actually do, grant us the rights to use your contribution. For details, visit 16 | https://cla.microsoft.com. 17 | 18 | When you submit a pull request, a CLA-bot will automatically determine whether you need 19 | to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the 20 | instructions provided by the bot. You will only need to do this once across all repositories using our CLA. 21 | 22 | ## Code of Conduct 23 | 24 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 25 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 26 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 27 | 28 | ## Reporting issues 29 | 30 | Have you identified a reproducible problem? 31 | Have a feature request? 32 | We want to hear about it! 33 | Here's how you can make reporting your issue as effective as possible. 34 | 35 | ### Look for an existing issue 36 | 37 | Before you create a new issue, please do a search in [open issues][issues] to see if the issue or feature request has already been filed. 38 | 39 | If you find your issue already exists, 40 | make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). 41 | Use a reaction in place of a "+1" comment: 42 | 43 | * 👍 - upvote 44 | 45 | ## Contributing to code 46 | 47 | - Before writing a fix or feature enhancement, ensure that an issue is logged. 48 | - Be prepared to discuss a feature and take feedback. 49 | - Include unit tests and updates documentation to complement the change. 50 | 51 | When you are ready to contribute a fix or feature: 52 | 53 | - Start by [forking the PSRule-vscode][github-fork]. 54 | - Create a new branch from main in your fork. 55 | - Add commits in your branch. 56 | - If you have updated module code or rules also update `CHANGELOG.md`. 57 | - You don't need to update the `CHANGELOG.md` for changes to unit tests or documentation. 58 | - Try building your changes locally. See [building from source][build] for instructions. 59 | - [Create a pull request][github-pr-create] to merge changes into the PSRule `main` branch. 60 | - If you are _ready_ for your changes to be reviewed create a _pull request_. 61 | - If you are _not ready_ for your changes to be reviewed, create a _draft pull request_. 62 | - An continuous integration (CI) process will automatically build your changes. 63 | - You changes must build successfully to be merged. 64 | - If you have any build errors, push new commits to your branch. 65 | - Avoid using forced pushes or squashing changes while in review, as this makes reviewing your changes harder. 66 | 67 | ### Intro to Git and GitHub 68 | 69 | When contributing to documentation or code changes, you'll need to have a GitHub account and a basic understanding of Git. 70 | Check out the links below to get started. 71 | 72 | - Make sure you have a [GitHub account][github-signup]. 73 | - GitHub Help: 74 | - [Git and GitHub learning resources][learn-git]. 75 | - [GitHub Flow Guide][github-flow]. 76 | - [Fork a repo][github-fork]. 77 | - [About Pull Requests][github-pr]. 78 | 79 | ### Code editor 80 | 81 | You should use the multi-platform [Visual Studio Code][vscode] (VS Code). 82 | The project contains a number of workspace specific settings that make it easier to author consistently. 83 | 84 | ### Building and testing 85 | 86 | When creating a pull request to merge your changes, a continuous integration (CI) pipeline is run. 87 | The CI pipeline will build then test your changes across MacOS, Linux and Windows configurations. 88 | 89 | Before opening a pull request try building your changes locally. 90 | 91 | ## Thank You! 92 | 93 | Your contributions to open source, large or small, make great projects like this possible. 94 | Thank you for taking the time to contribute. 95 | 96 | [learn-git]: https://help.github.com/en/articles/git-and-github-learning-resources 97 | [github-flow]: https://guides.github.com/introduction/flow/ 98 | [github-signup]: https://github.com/signup/free 99 | [github-fork]: https://help.github.com/en/github/getting-started-with-github/fork-a-repo 100 | [github-pr]: https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests 101 | [github-pr-create]: https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork 102 | [vscode]: https://code.visualstudio.com/ 103 | [issues]: https://github.com/Microsoft/PSRule-vscode/issues 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSRule 2 | 3 | Validate infrastructure as code (IaC) and DevOps repositories using the PSRule PowerShell module. 4 | PSRule is powerful, feature rich, and highly customizable to meet your needs. 5 | 6 | > [!NOTE] 7 | > This extension has being migrated into 8 | > and this repository will be archived in the future. 9 | > Currently this repository reflects the last stable release of v2.9.x. 10 | > An improve PSRule extension for V3 will be release from the new repository. 11 | > Please log an new issues at 12 | --- 13 | 14 | This extension is available in two release channels: 15 | 16 | Channel | Description | Version/ downloads 17 | ------- | ----------- | --- 18 | [Preview][ext-preview] | More frequent releases but more likely to contain bugs. | [![Preview][ext-preview-version-badge]][ext-preview] ![ext-preview-installs-badge] 19 | [Stable][ext-stable] | Less frequent releases, with more user testing, experimental features are disabled. | [![Stable][ext-stable-version-badge]][ext-stable] ![ext-stable-installs-badge] 20 | 21 | ## Features 22 | 23 | ### CodeLens 24 | 25 |

26 | CodeLens showing link to create documentation 27 |

28 | 29 | - CodeLens shows links to create or edit markdown documentation from rules in YAML, JSON, or PowerShell. 30 | - **Open documentation** — Opens rule markdown documentation in the editor. 31 | - The location for storing documentation is configurable in the extension settings. 32 | - By default, a locale specific folder is created in the same directory as the rule file. 33 | - **Create documentation** — Creates a new markdown file for the rule based on a snippet. 34 | - New markdown documentation is created with the built-in _Rule Doc_ snippet. 35 | - An alternative snippet can be specified by configuring extension settings. 36 | 37 | ### IntelliSense 38 | 39 |

40 | Options suggestion context menu 41 |

42 | 43 | - Adds IntelliSense and validation support for configuring options and resources. 44 | - **Workspace options** — use IntelliSense to configure options for the workspace. 45 | - Type or trigger IntelliSense with `Ctrl+Space` from `ps-rule.yaml`. 46 | - **Create resources** — define _baselines_ and _selectors_ by using pre-built snippets and IntelliSense. 47 | 48 |

49 | Rule definition snippet 50 |

51 | 52 | - Adds snippets for defining new rules. 53 | - **Define rules** with snippets and IntelliSense support. 54 | - Trigger IntelliSense by typing `rule` in a `.Rule.ps1`, `.Rule.yaml`, or `.Rule.jsonc` file. 55 | IntelliSense can also be triggered by using the shortcut `Ctrl+Space`. 56 | 57 |

58 | Rule markdown documentation snippet 59 |

60 | 61 | - Adds snippets for creating markdown documentation. 62 | - **Quick documentation** — create rule documentation to provide rule recommendations and examples. 63 | - Trigger IntelliSense by typing `rule` in a `.md` file. 64 | IntelliSense can also be triggered by using the shortcut `Ctrl+Space`. 65 | 66 | ### Quick tasks 67 | 68 |

69 | Built-in tasks shown in task list 70 |

71 | 72 | - Adds quick tasks for analysis directly from Visual Studio Code. 73 | - **Run analysis** — runs rules against files in the current workspace. 74 | - _Input path_, _Baseline_, _Modules_, and _Outcome_ options can be configured per task. 75 | - _Output as_, and showing a _Not processed warning_ options can be configured by workspace or user. 76 | - Rule stored in `.ps-rule/` are automatically used by default. 77 | - Use the built-in analysis task by running or configuring the task from the _Terminal_ menu. 78 | 79 | ## Configuration 80 | 81 | In addition to configuring the [ps-rule.yaml] options file, the following settings are available. 82 | 83 | Name | Description 84 | ---- | ----------- 85 | `PSRule.codeLens.ruleDocumentationLinks` | Enables Code Lens that displays links to rule documentation. This is an experimental feature that requires experimental features to be enabled. 86 | `PSRule.documentation.path` | The path to look for rule documentation. When not set, the path containing rules will be used. 87 | `PSRule.documentation.localePath` | The locale path to use for locating rule documentation. The VS Code locale will be used by default. 88 | `PSRule.documentation.customSnippetPath` | The path to a file containing a rule documentation snippet. When not set, built-in PSRule snippets will be used. 89 | `PSRule.documentation.snippet` | The name of a snippet to use when creating new rule documentation. By default, the built-in `Rule Doc` snippet will be used. 90 | `PSRule.execution.notProcessedWarning` | Warn when objects are not processed by any rule. This option is deprecated and replaced by `PSRule.execution.unprocessedObject`. 91 | `PSRule.execution.ruleExcluded` | Determines how to handle excluded rules. When set to `None`, PSRule will use the default (`Ignore`), unless set by PSRule options. 92 | `PSRule.execution.ruleSuppressed` | Determines how to handle suppressed rules. When set to `None`, PSRule will use the default (`Warn`), unless set by PSRule options. 93 | `PSRule.execution.unprocessedObject` | Determines how to report objects that are not processed by any rule. When set to `None`, PSRule will use the default (`Warn`), unless set by PSRule options. 94 | `PSRule.experimental.enabled` | Enables experimental features in the PSRule extension. 95 | `PSRule.notifications.showChannelUpgrade` | Determines if a notification to switch to the stable channel is shown on start up. 96 | `PSRule.notifications.showPowerShellExtension` | Determines if a notification to install the PowerShell extension is shown on start up. 97 | `PSRule.output.as` | Configures the output of analysis tasks, either summary or detailed. 98 | `PSRule.rule.baseline` | The name of the default baseline to use for executing rules. This setting can be overridden on individual PSRule tasks. 99 | 100 | ## Support 101 | 102 | This project uses GitHub Issues to track bugs and feature requests. 103 | Please search the existing issues before filing new issues to avoid duplicates. 104 | 105 | - For new issues, file your bug or feature request as a new [issue]. 106 | - For help, discussion, and support questions about using this project, join or start a [discussion]. 107 | 108 | Support for this project/ product is limited to the resources listed above. 109 | 110 | ## Installing PSRule module 111 | 112 | PSRule is available from the PowerShell Gallery and is required for this extension to work. 113 | 114 | To install the module use the following command from a PowerShell prompt. 115 | 116 | ```powershell 117 | Install-Module -Name PSRule -Scope CurrentUser; 118 | ``` 119 | 120 | ## Installing the extension 121 | 122 | You can install the latest release of the extension by following the steps in the [Visual Studio Code documentation][vscode-ext-gallery]. 123 | In the Extensions pane, search for _PSRule_ extension and install it there. 124 | You will get notified automatically about any future extension updates. 125 | 126 | ```text 127 | code --install-extension bewhite.psrule-vscode-preview 128 | ``` 129 | 130 | > NOTE: If you are using VS Code Insiders, the command will be `code-insiders`. 131 | 132 | ## Contributing 133 | 134 | This project welcomes contributions and suggestions. 135 | If you are ready to contribute, please visit the [contribution guide]. 136 | 137 | ## Code of Conduct 138 | 139 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 140 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 141 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 142 | 143 | ## Maintainers 144 | 145 | - [Bernie White](https://github.com/BernieWhite) 146 | 147 | ## License 148 | 149 | This project is [licensed under the MIT License][license]. 150 | 151 | [issue]: https://github.com/Microsoft/PSRule-vscode/issues 152 | [discussion]: https://github.com/microsoft/PSRule-vscode/discussions 153 | [vscode-ext-gallery]: https://code.visualstudio.com/docs/editor/extension-gallery 154 | [ext-preview]: https://marketplace.visualstudio.com/items?itemName=bewhite.psrule-vscode-preview 155 | [ext-preview-version-badge]: https://vsmarketplacebadges.dev/version/bewhite.psrule-vscode-preview.png 156 | [ext-preview-installs-badge]: https://vsmarketplacebadges.dev/installs-short/bewhite.psrule-vscode-preview.png 157 | [ext-stable]: https://marketplace.visualstudio.com/items?itemName=bewhite.psrule-vscode 158 | [ext-stable-version-badge]: https://vsmarketplacebadges.dev/version/bewhite.psrule-vscode.png 159 | [ext-stable-installs-badge]: https://vsmarketplacebadges.dev/installs-short/bewhite.psrule-vscode.png 160 | [contribution guide]: https://github.com/Microsoft/PSRule-vscode/blob/main/CONTRIBUTING.md 161 | [license]: https://github.com/Microsoft/PSRule-vscode/blob/main/LICENSE 162 | [ps-rule.yaml]: https://aka.ms/ps-rule/options 163 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security policy 2 | 3 | 4 | 5 | ## Security 6 | 7 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 8 | 9 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 10 | 11 | ## Reporting Security Issues 12 | 13 | **Please do not report security vulnerabilities through public GitHub issues.** 14 | 15 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 16 | 17 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 18 | 19 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 20 | 21 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 22 | 23 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 24 | * Full paths of source file(s) related to the manifestation of the issue 25 | * The location of the affected source code (tag/branch/commit or direct URL) 26 | * Any special configuration required to reproduce the issue 27 | * Step-by-step instructions to reproduce the issue 28 | * Proof-of-concept or exploit code (if possible) 29 | * Impact of the issue, including how an attacker might exploit the issue 30 | 31 | This information will help us triage your report more quickly. 32 | 33 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 34 | 35 | ## Preferred Languages 36 | 37 | We prefer all communications to be in English. 38 | 39 | ## Policy 40 | 41 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 42 | 43 | 44 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. 6 | Please search the existing issues before filing new issues to avoid duplicates. 7 | 8 | - For new issues, file your bug or feature request as a new [issue]. 9 | - For help, discussion, and support questions about using this project, join or start a [discussion]. 10 | 11 | ## Microsoft Support Policy 12 | 13 | Support for this project/ product is limited to the resources listed above. 14 | 15 | [issue]: https://github.com/microsoft/PSRule-vscode/issues 16 | [discussion]: https://github.com/microsoft/PSRule-vscode/discussions 17 | -------------------------------------------------------------------------------- /docs/Example.Rule.jsonc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | [ 5 | { 6 | // Synopsis: This is an example baseline 7 | "apiVersion": "github.com/microsoft/PSRule/v1", 8 | "kind": "Baseline", 9 | "metadata": { 10 | "name": "Module4" 11 | }, 12 | "spec": { 13 | "binding": { 14 | "field": { 15 | "kind": ["Id"], 16 | "uniqueIdentifer": ["AlternateName", "Id"] 17 | }, 18 | "targetName": ["AlternateName"], 19 | "targetType": ["Kind"] 20 | }, 21 | "configuration": { 22 | "ruleConfig1": "Test" 23 | }, 24 | "rule": { 25 | "include": ["M4.Rule1"] 26 | } 27 | } 28 | }, 29 | { 30 | // Synopsis: This is an example baseline 31 | "apiVersion": "github.com/microsoft/PSRule/v1", 32 | "kind": "Baseline", 33 | "metadata": { 34 | "name": "Baseline2" 35 | }, 36 | "spec": { 37 | "binding": { 38 | "targetName": ["AlternateName"], 39 | "targetType": ["Kind"] 40 | }, 41 | "configuration": { 42 | "ruleConfig2": "Test3" 43 | }, 44 | "rule": { 45 | "include": ["M4.Rule1"] 46 | } 47 | } 48 | }, 49 | { 50 | // Synopsis: This is an example baseline 51 | "apiVersion": "github.com/microsoft/PSRule/v1", 52 | "kind": "Baseline", 53 | "metadata": { 54 | "name": "Baseline3" 55 | }, 56 | "spec": { 57 | "binding": { 58 | "field": { 59 | "AlternativeType": ["AlternateName"] 60 | }, 61 | "targetName": ["AlternateName"], 62 | "targetType": ["Kind"] 63 | }, 64 | "configuration": { 65 | "ruleConfig2": "Test3" 66 | }, 67 | "rule": { 68 | "include": ["M4.Rule1"] 69 | } 70 | } 71 | } 72 | ] 73 | -------------------------------------------------------------------------------- /docs/example.Rule.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Example validation rules 6 | # 7 | 8 | # Description: Redis Cache should only accept secure connections 9 | Rule 'redis.NonSslPort' -If { ResourceType 'Microsoft.Cache/Redis' } { 10 | $TargetObject.properties.enableNonSslPort -eq $False 11 | } 12 | 13 | # Synopsis: Redis Cache should reject TLS versions older then 1.2 14 | Rule 'redis.MinTLS' -Type 'Microsoft.Cache/Redis' { # Synopsis: ddd 15 | Recommend 'Enforce TLS 1.2 unless required to support older tools.' 16 | 17 | # Check that TLS is within range 18 | Within 'properties.minimumTlsVersion' '1.2' 19 | } 20 | -------------------------------------------------------------------------------- /docs/example.Rule.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | # Synopsis: An example baseline 4 | apiVersion: github.com/microsoft/PSRule/v1 5 | kind: Baseline 6 | metadata: 7 | name: ExampleBaseline 8 | spec: 9 | rule: 10 | exclude: 11 | - Rule1 12 | 13 | --- 14 | # Synopsis: An example module configuration 15 | apiVersion: github.com/microsoft/PSRule/v1 16 | kind: ModuleConfig 17 | metadata: 18 | name: ExampleConfiguration 19 | spec: 20 | binding: { } 21 | configuration: { } 22 | output: 23 | culture: 24 | - en-US 25 | 26 | --- 27 | # Synopsis: An example selector 28 | apiVersion: github.com/microsoft/PSRule/v1 29 | kind: Selector 30 | metadata: 31 | name: ExampleSelector 32 | spec: 33 | if: 34 | allOf: 35 | - field: Name 36 | equals: TargetObject1 37 | - field: Value 38 | equals: value1 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/images/codelens-doc-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSRule-vscode/121a3a90baa50e7b90e203464e62f4adf8e44f6b/docs/images/codelens-doc-link.png -------------------------------------------------------------------------------- /docs/images/options-schema-flyout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSRule-vscode/121a3a90baa50e7b90e203464e62f4adf8e44f6b/docs/images/options-schema-flyout.png -------------------------------------------------------------------------------- /docs/images/snippet-markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSRule-vscode/121a3a90baa50e7b90e203464e62f4adf8e44f6b/docs/images/snippet-markdown.png -------------------------------------------------------------------------------- /docs/images/snippet-rule-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSRule-vscode/121a3a90baa50e7b90e203464e62f4adf8e44f6b/docs/images/snippet-rule-type.png -------------------------------------------------------------------------------- /docs/images/tasks-provider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSRule-vscode/121a3a90baa50e7b90e203464e62f4adf8e44f6b/docs/images/tasks-provider.png -------------------------------------------------------------------------------- /docs/ps-rule.yaml: -------------------------------------------------------------------------------- 1 | 2 | execution: 3 | languageMode: FullLanguage 4 | 5 | -------------------------------------------------------------------------------- /extension.md: -------------------------------------------------------------------------------- 1 | # PSRule 2 | 3 | Validate infrastructure as code (IaC) and DevOps repositories using the PSRule PowerShell module. 4 | PSRule is powerful, feature rich, and highly customizable to meet your needs. 5 | 6 | ![module-version-badge] 7 | 8 | This extension is available in two release channels: 9 | 10 | Channel | Description | Version/ downloads 11 | ------- | ----------- | --- 12 | [Preview][ext-preview] | More frequent releases but more likely to contain bugs. | [![Preview][ext-preview-version-badge]][ext-preview] ![ext-preview-installs-badge] 13 | [Stable][ext-stable] | Less frequent releases, with more user testing, experimental features are disabled. | [![Stable][ext-stable-version-badge]][ext-stable] ![ext-stable-installs-badge] 14 | 15 | ## Features 16 | 17 | ### CodeLens 18 | 19 |

20 | CodeLens showing link to create documentation 21 |

22 | 23 | - CodeLens shows links to create or edit markdown documentation from rules in YAML, JSON, or PowerShell. 24 | - **Open documentation** — Opens rule markdown documentation in the editor. 25 | - The location for storing documentation is configurable in the extension settings. 26 | - By default, a locale specific folder is created in the same directory as the rule file. 27 | - **Create documentation** — Creates a new markdown file for the rule based on a snippet. 28 | - New markdown documentation is created with the built-in _Rule Doc_ snippet. 29 | - An alternative snippet can be specified by configuring extension settings. 30 | 31 | ### IntelliSense 32 | 33 |

34 | Options suggestion context menu 35 |

36 | 37 | - Adds IntelliSense and validation support for configuring options and resources. 38 | - **Workspace options** — use IntelliSense to configure options for the workspace. 39 | - Type or trigger IntelliSense with `Ctrl+Space` from `ps-rule.yaml`. 40 | - **Create resources** — define _baselines_ and _selectors_ by using pre-built snippets and IntelliSense. 41 | 42 |

43 | Rule definition snippet 44 |

45 | 46 | - Adds snippets for defining new rules. 47 | - **Define rules** with snippets and IntelliSense support. 48 | - Trigger IntelliSense by typing `rule` in a `.Rule.ps1`, `.Rule.yaml`, or `.Rule.jsonc` file. 49 | IntelliSense can also be triggered by using the shortcut `Ctrl+Space`. 50 | 51 |

52 | Rule markdown documentation snippet 53 |

54 | 55 | - Adds snippets for creating markdown documentation. 56 | - **Quick documentation** — create rule documentation to provide rule recommendations and examples. 57 | - Trigger IntelliSense by typing `rule` in a `.md` file. 58 | IntelliSense can also be triggered by using the shortcut `Ctrl+Space`. 59 | 60 | ### Quick tasks 61 | 62 |

63 | Built-in tasks shown in task list 64 |

65 | 66 | - Adds quick tasks for analysis directly from Visual Studio Code. 67 | - **Run analysis** — runs rules against files in the current workspace. 68 | - _Input path_, _Baseline_, _Modules_, and _Outcome_ options can be configured per task. 69 | - _Output as_, and showing a _Not processed warning_ options can be configured by workspace or user. 70 | - Rule stored in `.ps-rule/` are automatically used by default. 71 | - Use the built-in analysis task by running or configuring the task from the _Terminal_ menu. 72 | 73 | ## Configuration 74 | 75 | In addition to configuring the [ps-rule.yaml] options file, the following settings are available. 76 | 77 | Name | Description 78 | ---- | ----------- 79 | `PSRule.codeLens.ruleDocumentationLinks` | Enables Code Lens that displays links to rule documentation. This is an experimental feature that requires experimental features to be enabled. 80 | `PSRule.documentation.path` | The path to look for rule documentation. When not set, the path containing rules will be used. 81 | `PSRule.documentation.localePath` | The locale path to use for locating rule documentation. The VS Code locale will be used by default. 82 | `PSRule.documentation.customSnippetPath` | The path to a file containing a rule documentation snippet. When not set, built-in PSRule snippets will be used. 83 | `PSRule.documentation.snippet` | The name of a snippet to use when creating new rule documentation. By default, the built-in `Rule Doc` snippet will be used. 84 | `PSRule.execution.notProcessedWarning` | Warn when objects are not processed by any rule. This option is deprecated and replaced by `PSRule.execution.unprocessedObject`. 85 | `PSRule.execution.ruleExcluded` | Determines how to handle excluded rules. When set to `None`, PSRule will use the default (`Ignore`), unless set by PSRule options. 86 | `PSRule.execution.ruleSuppressed` | Determines how to handle suppressed rules. When set to `None`, PSRule will use the default (`Warn`), unless set by PSRule options. 87 | `PSRule.execution.unprocessedObject` | Determines how to report objects that are not processed by any rule. When set to `None`, PSRule will use the default (`Warn`), unless set by PSRule options. 88 | `PSRule.experimental.enabled` | Enables experimental features in the PSRule extension. 89 | `PSRule.notifications.showChannelUpgrade` | Determines if a notification to switch to the stable channel is shown on start up. 90 | `PSRule.notifications.showPowerShellExtension` | Determines if a notification to install the PowerShell extension is shown on start up. 91 | `PSRule.output.as` | Configures the output of analysis tasks, either summary or detailed. 92 | `PSRule.rule.baseline` | The name of the default baseline to use for executing rules. This setting can be overridden on individual PSRule tasks. 93 | 94 | ## Support 95 | 96 | This project uses GitHub Issues to track bugs and feature requests. 97 | Please search the existing issues before filing new issues to avoid duplicates. 98 | 99 | - For new issues, file your bug or feature request as a new [issue]. 100 | - For help, discussion, and support questions about using this project, join or start a [discussion]. 101 | 102 | Support for this project/ product is limited to the resources listed above. 103 | 104 | ## Installing PSRule module 105 | 106 | PSRule is available from the PowerShell Gallery and is required for this extension to work. 107 | 108 | To install the module use the following command from a PowerShell prompt. 109 | 110 | ```powershell 111 | Install-Module -Name PSRule -Scope CurrentUser; 112 | ``` 113 | 114 | ## Installing the extension 115 | 116 | You can install the latest release of the extension by following the steps in the [Visual Studio Code documentation][vscode-ext-gallery]. 117 | In the Extensions pane, search for _PSRule_ extension and install it there. 118 | You will get notified automatically about any future extension updates. 119 | 120 | ```text 121 | code --install-extension bewhite.psrule-vscode-preview 122 | ``` 123 | 124 | > NOTE: If you are using VS Code Insiders, the command will be `code-insiders`. 125 | 126 | ## Contributing 127 | 128 | This project welcomes contributions and suggestions. 129 | If you are ready to contribute, please visit the [contribution guide]. 130 | 131 | ## Code of Conduct 132 | 133 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 134 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 135 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 136 | 137 | ## Maintainers 138 | 139 | - [Bernie White](https://github.com/BernieWhite) 140 | 141 | ## License 142 | 143 | This project is [licensed under the MIT License][license]. 144 | 145 | [issue]: https://github.com/Microsoft/PSRule-vscode/issues 146 | [discussion]: https://github.com/microsoft/PSRule-vscode/discussions 147 | [vscode-ext-gallery]: https://code.visualstudio.com/docs/editor/extension-gallery 148 | [ext-preview]: https://marketplace.visualstudio.com/items?itemName=bewhite.psrule-vscode-preview 149 | [ext-preview-version-badge]: https://vsmarketplacebadges.dev/version/bewhite.psrule-vscode-preview.png 150 | [ext-preview-installs-badge]: https://vsmarketplacebadges.dev/installs-short/bewhite.psrule-vscode-preview.png 151 | [ext-stable]: https://marketplace.visualstudio.com/items?itemName=bewhite.psrule-vscode 152 | [ext-stable-version-badge]: https://vsmarketplacebadges.dev/version/bewhite.psrule-vscode.png 153 | [ext-stable-installs-badge]: https://vsmarketplacebadges.dev/installs-short/bewhite.psrule-vscode.png 154 | [module-version-badge]: https://img.shields.io/powershellgallery/v/PSRule.png?label=PowerShell%20Gallery&color=brightgreen 155 | [contribution guide]: https://github.com/Microsoft/PSRule-vscode/blob/main/CONTRIBUTING.md 156 | [license]: https://github.com/Microsoft/PSRule-vscode/blob/main/LICENSE 157 | [ps-rule.yaml]: https://aka.ms/ps-rule/options 158 | -------------------------------------------------------------------------------- /media/icon256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSRule-vscode/121a3a90baa50e7b90e203464e62f4adf8e44f6b/media/icon256.png -------------------------------------------------------------------------------- /media/walkthroughs/getStarted/01_configureOptions.md: -------------------------------------------------------------------------------- 1 | # Configure an options file 2 | 3 | - First, type `requires` and press Tab or Enter to complete the suggestion. 4 | - Continue typing to replace `Module` with `PSRule` and press Tab or Enter to move to the version constraint. 5 | - Replace `'>=1.0.0'` with the minimum version of PSRule you want to use, such as `'>=2.8.1'`. 6 | 7 | ## Configure additional modules 8 | 9 | Additional modules can be added to the options file. 10 | For example: 11 | 12 | [Copy snippet](command:PSRule.walkthroughCopySnippet?%7B%22snippet%22%3A%2201_requiresModules%22%7D) 13 | 14 | ```yaml 15 | requires: 16 | PSRule: '>=2.8.1' 17 | PSRule.Rules.Azure: '>=1.26.1' 18 | ``` 19 | 20 | ### Configure other options 21 | 22 | Rules often have additional configuration options that can be set. 23 | For example, the `PSRule.Rules.Azure` module has the following options: 24 | 25 | [Copy snippet](command:PSRule.walkthroughCopySnippet?%7B%22snippet%22%3A%2201_configureAzure%22%7D) 26 | 27 | ```yaml 28 | configuration: 29 | # Enable expansion of Azure Template parameter files. 30 | AZURE_PARAMETER_FILE_EXPANSION: true 31 | 32 | # Enable expansion of Azure Bicep files. 33 | AZURE_BICEP_FILE_EXPANSION: true 34 | ``` 35 | -------------------------------------------------------------------------------- /media/walkthroughs/getStarted/02_configureSettings.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSRule-vscode/121a3a90baa50e7b90e203464e62f4adf8e44f6b/media/walkthroughs/getStarted/02_configureSettings.md -------------------------------------------------------------------------------- /media/walkthroughs/getStarted/03_runTasks.md: -------------------------------------------------------------------------------- 1 | # Run analysis 2 | 3 | Run tasks within Visual Studio Code to analyze the Infrastructure as Code within your workspace. 4 | 5 | ## Run tasks 6 | 7 | - Open the **Command Palette** (Ctrl+Shift+P) and select **Tasks: Run Task**. 8 | - Choose or type **PSRule**. 9 | - Then select **PSRule: Run analysis**. 10 | - Optionally, select **PSRule** to scan the task output to collect error or warnings. 11 | 12 | ![Run tasks](03_runTasks.svg) 13 | 14 | ## Configuring tasks 15 | 16 | Additional configuration options can be set on the **PSRule: Run analysis** task. 17 | Such as the input path, modules, baseline, and additional environment variables by configuring the `tasks.json`. 18 | -------------------------------------------------------------------------------- /media/walkthroughs/getStarted/03_runTasks.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /media/walkthroughs/getStarted/04_learnMore.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSRule-vscode/121a3a90baa50e7b90e203464e62f4adf8e44f6b/media/walkthroughs/getStarted/04_learnMore.md -------------------------------------------------------------------------------- /media/walkthroughs/getStarted/snippets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "01_requiresModules", 4 | "snippet": [ 5 | "requires:", 6 | " PSRule: '>=2.8.1'", 7 | " PSRule.Rules.Azure: '>=1.26.1'" 8 | ] 9 | }, 10 | { 11 | "name": "01_configureAzure", 12 | "snippet": [ 13 | "configuration:", 14 | " # Enable expansion of Azure Template parameter files.", 15 | " AZURE_PARAMETER_FILE_EXPANSION: true", 16 | "", 17 | " # Enable expansion of Azure Bicep files.", 18 | " AZURE_BICEP_FILE_EXPANSION: true" 19 | ] 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "psrule-vscode", 3 | "displayName": "PSRule V2", 4 | "version": "2.9.1", 5 | "publisher": "bewhite", 6 | "description": "Validate infrastructure as code (IaC) and DevOps repositories using PSRule.", 7 | "author": { 8 | "name": "Bernie White" 9 | }, 10 | "engines": { 11 | "vscode": "^1.78.0" 12 | }, 13 | "license": "SEE LICENSE IN LICENSE", 14 | "homepage": "https://github.com/microsoft/PSRule-vscode/blob/release/v2.9.x/extension.md", 15 | "categories": [ 16 | "Programming Languages", 17 | "Snippets" 18 | ], 19 | "keywords": [ 20 | "PowerShell", 21 | "json", 22 | "yaml" 23 | ], 24 | "galleryBanner": { 25 | "color": "#0072c6", 26 | "theme": "dark" 27 | }, 28 | "icon": "media/icon256.png", 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/microsoft/PSRule-vscode.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/microsoft/PSRule/issues" 35 | }, 36 | "private": true, 37 | "preview": false, 38 | "activationEvents": [ 39 | "onLanguage:powershell", 40 | "onLanguage:yaml", 41 | "workspaceContains:/ps-rule.yaml", 42 | "workspaceContains:**/ps-rule.yaml", 43 | "workspaceContains:**/*.Rule.yaml", 44 | "workspaceContains:**/*.Rule.yml", 45 | "workspaceContains:**/*.Rule.json", 46 | "workspaceContains:**/*.Rule.jsonc", 47 | "workspaceContains:**/*.Rule.ps1", 48 | "onCommand:workbench.action.tasks.runTask" 49 | ], 50 | "main": "./out/dist/main.js", 51 | "capabilities": { 52 | "untrustedWorkspaces": { 53 | "supported": "limited", 54 | "description": "PSRule in untrusted mode." 55 | } 56 | }, 57 | "contributes": { 58 | "commands": [ 59 | { 60 | "command": "PSRule.createOrEditDocumentation", 61 | "title": "Create or edit documentation", 62 | "shortTitle": "Edit documentation", 63 | "category": "PSRule" 64 | }, 65 | { 66 | "command": "PSRule.openOptionsFile", 67 | "title": "Open options file", 68 | "category": "PSRule" 69 | }, 70 | { 71 | "command": "PSRule.createOptionsFile", 72 | "title": "Create options file", 73 | "category": "PSRule" 74 | }, 75 | { 76 | "command": "PSRule.configureSettings", 77 | "title": "Configure settings", 78 | "category": "PSRule" 79 | }, 80 | { 81 | "$comment": "Only used by walkthrough.", 82 | "command": "PSRule.walkthroughCopySnippet", 83 | "title": "Copy walkthrough snippet", 84 | "enablement": "false", 85 | "category": "PSRule" 86 | }, 87 | { 88 | "command": "PSRule.runAnalysisTask", 89 | "title": "Run analysis", 90 | "category": "PSRule" 91 | }, 92 | { 93 | "command": "PSRule.showTasks", 94 | "title": "Show tasks", 95 | "enablement": "false", 96 | "category": "PSRule" 97 | } 98 | ], 99 | "configuration": [ 100 | { 101 | "title": "PSRule", 102 | "properties": { 103 | "PSRule.codeLens.ruleDocumentationLinks": { 104 | "type": "boolean", 105 | "default": true, 106 | "description": "Enables Code Lens that displays links to rule documentation.", 107 | "scope": "application" 108 | }, 109 | "PSRule.documentation.path": { 110 | "type": "string", 111 | "default": null, 112 | "description": "The path to look for rule documentation. When not set, the path containing rules will be used.", 113 | "scope": "window" 114 | }, 115 | "PSRule.documentation.localePath": { 116 | "type": "string", 117 | "default": null, 118 | "description": "The locale path to use for locating rule documentation. The VS Code locale will be used by default.", 119 | "scope": "window" 120 | }, 121 | "PSRule.documentation.customSnippetPath": { 122 | "type": "string", 123 | "default": null, 124 | "description": "The path to a file containing a rule documentation snippet. When not set, built-in PSRule snippets will be used.", 125 | "scope": "window" 126 | }, 127 | "PSRule.documentation.snippet": { 128 | "type": "string", 129 | "default": "Rule Doc", 130 | "markdownDescription": "The name of a snippet to use when creating new rule documentation. By default, the built-in `Rule Doc` snippet will be used.", 131 | "scope": "window" 132 | }, 133 | "PSRule.execution.notProcessedWarning": { 134 | "type": "boolean", 135 | "default": false, 136 | "description": "Warn when objects are not processed by any rule. Supported on PSRule < v3.", 137 | "scope": "window", 138 | "markdownDeprecationMessage": "This option is replaced by [Unprocessed Object](https://aka.ms/ps-rule/options#executionunprocessedobject) from PSRule v2.9.0. This option will be removed from PSRule v3." 139 | }, 140 | "PSRule.execution.ruleExcluded": { 141 | "type": "string", 142 | "default": "None", 143 | "markdownDescription": "Determines how to handle [excluded rules](https://aka.ms/ps-rule/options#executionruleexcluded). When set to `None`, PSRule will use the default (`Ignore`), unless set by [PSRule options](https://aka.ms/ps-rule/options#executionruleexcluded).", 144 | "markdownEnumDescriptions": [ 145 | "Excluded rules will not generate any notifications unless overridden.", 146 | "Excluded rules will not generate any notifications.", 147 | "Excluded rules will generate a warning.", 148 | "Excluded rules will generate an error." 149 | ], 150 | "enum": [ 151 | "None", 152 | "Ignore", 153 | "Warn", 154 | "Error" 155 | ], 156 | "scope": "application" 157 | }, 158 | "PSRule.execution.ruleSuppressed": { 159 | "type": "string", 160 | "default": "None", 161 | "markdownDescription": "Determines how to handle [suppressed rules](https://aka.ms/ps-rule/options#executionrulesuppressed). When set to `None`, PSRule will use the default (`Warn`), unless set by [PSRule options](https://aka.ms/ps-rule/options#executionrulesuppressed).", 162 | "markdownEnumDescriptions": [ 163 | "Suppressed rules will generate a warning unless overridden.", 164 | "Suppressed rules will not generate any notifications.", 165 | "Suppressed rules will generate a warning.", 166 | "Suppressed rules will generate an error." 167 | ], 168 | "enum": [ 169 | "None", 170 | "Ignore", 171 | "Warn", 172 | "Error" 173 | ], 174 | "scope": "application" 175 | }, 176 | "PSRule.execution.unprocessedObject": { 177 | "type": "string", 178 | "default": "None", 179 | "markdownDescription": "Determines how to report objects that are [not processed by any rule](https://aka.ms/ps-rule/options#executionunprocessedobject). When set to `None`, PSRule will use the default (`Warn`), unless set by [PSRule options](https://aka.ms/ps-rule/options#executionunprocessedobject).", 180 | "markdownEnumDescriptions": [ 181 | "Suppressed rules will generate a warning unless overridden.", 182 | "Suppressed rules will not generate any notifications.", 183 | "Suppressed rules will generate a warning.", 184 | "Suppressed rules will generate an error." 185 | ], 186 | "enum": [ 187 | "None", 188 | "Ignore", 189 | "Warn", 190 | "Error" 191 | ], 192 | "scope": "application" 193 | }, 194 | "PSRule.experimental.enabled": { 195 | "type": "boolean", 196 | "default": false, 197 | "description": "Enables experimental features in the PSRule extension.", 198 | "scope": "application" 199 | }, 200 | "PSRule.notifications.showChannelUpgrade": { 201 | "type": "boolean", 202 | "default": true, 203 | "description": "Determines if a notification to switch to the stable channel is shown on start up.", 204 | "scope": "application" 205 | }, 206 | "PSRule.notifications.showPowerShellExtension": { 207 | "type": "boolean", 208 | "default": true, 209 | "description": "Determines if a notification to install the PowerShell extension is shown on start up.", 210 | "scope": "application" 211 | }, 212 | "PSRule.output.as": { 213 | "type": "string", 214 | "default": "Summary", 215 | "description": "Configures the output of analysis tasks, either summary or detailed.", 216 | "enum": [ 217 | "Detail", 218 | "Summary" 219 | ], 220 | "scope": "window" 221 | }, 222 | "PSRule.rule.baseline": { 223 | "type": "string", 224 | "default": null, 225 | "description": "The name of the default baseline to use for executing rules. This setting can be overridden on individual PSRule tasks.", 226 | "scope": "window" 227 | } 228 | } 229 | } 230 | ], 231 | "taskDefinitions": [ 232 | { 233 | "type": "PSRule", 234 | "required": [], 235 | "properties": { 236 | "path": { 237 | "type": "string", 238 | "description": "The path containing rules.", 239 | "default": "./.ps-rule/" 240 | }, 241 | "inputPath": { 242 | "type": "string", 243 | "description": "The path PSRule will look for input files. Defaults to workspace root.", 244 | "default": "." 245 | }, 246 | "baseline": { 247 | "type": "string", 248 | "description": "The name of a PSRule baseline to use. Baselines can be used from modules or specified in a separate file. This option overrides the default baseline setting set for a workspace or user." 249 | }, 250 | "modules": { 251 | "type": "array", 252 | "description": "The name of one or more modules to use." 253 | }, 254 | "outcome": { 255 | "type": "array", 256 | "items": { 257 | "enum": [ 258 | "Pass", 259 | "Fail", 260 | "Error" 261 | ] 262 | }, 263 | "default": [ 264 | "Fail", 265 | "Error" 266 | ] 267 | } 268 | } 269 | } 270 | ], 271 | "snippets": [ 272 | { 273 | "language": "powershell", 274 | "path": "./snippets/powershell.json" 275 | }, 276 | { 277 | "language": "markdown", 278 | "path": "./snippets/markdown.json" 279 | }, 280 | { 281 | "language": "yaml", 282 | "path": "./snippets/yaml.json" 283 | }, 284 | { 285 | "language": "yaml", 286 | "path": "./snippets/github-snippets.json" 287 | }, 288 | { 289 | "language": "yaml", 290 | "path": "./snippets/pipelines-snippets.json" 291 | }, 292 | { 293 | "language": "json", 294 | "path": "./snippets/json.json" 295 | }, 296 | { 297 | "language": "jsonc", 298 | "path": "./snippets/json.json" 299 | } 300 | ], 301 | "grammars": [ 302 | { 303 | "path": "./syntaxes/comments.json", 304 | "scopeName": "PSRule-powershell-comments", 305 | "injectTo": [ 306 | "source.powershell" 307 | ] 308 | }, 309 | { 310 | "path": "./syntaxes/keywords.json", 311 | "scopeName": "PSRule-powershell-keywords", 312 | "injectTo": [ 313 | "source.powershell" 314 | ] 315 | }, 316 | { 317 | "path": "./syntaxes/rule.json", 318 | "scopeName": "PSRule-powershell-rule", 319 | "injectTo": [ 320 | "source.powershell" 321 | ] 322 | }, 323 | { 324 | "path": "./syntaxes/yaml-comments.json", 325 | "scopeName": "PSRule-yaml-comments", 326 | "injectTo": [ 327 | "source.yaml" 328 | ] 329 | } 330 | ], 331 | "yamlValidation": [ 332 | { 333 | "fileMatch": "ps-rule.yaml", 334 | "url": "./schemas/PSRule-options.schema.json" 335 | }, 336 | { 337 | "fileMatch": "ps-rule.yml", 338 | "url": "./schemas/PSRule-options.schema.json" 339 | }, 340 | { 341 | "fileMatch": "psrule.yaml", 342 | "url": "./schemas/PSRule-options.schema.json" 343 | }, 344 | { 345 | "fileMatch": "psrule.yml", 346 | "url": "./schemas/PSRule-options.schema.json" 347 | }, 348 | { 349 | "fileMatch": "**/*.Rule.yaml", 350 | "url": "./schemas/PSRule-language.schema.json" 351 | }, 352 | { 353 | "fileMatch": "**/*.Rule.yml", 354 | "url": "./schemas/PSRule-language.schema.json" 355 | } 356 | ], 357 | "jsonValidation": [ 358 | { 359 | "fileMatch": "*.Rule.json", 360 | "url": "./schemas/PSRule-resources.schema.json" 361 | }, 362 | { 363 | "fileMatch": "*.Rule.jsonc", 364 | "url": "./schemas/PSRule-resources.schema.json" 365 | } 366 | ], 367 | "problemMatchers": [ 368 | { 369 | "name": "PSRule", 370 | "label": "PSRule", 371 | "owner": "PSRule", 372 | "source": "PSRule", 373 | "severity": "error", 374 | "fileLocation": [ 375 | "relative", 376 | "${workspaceFolder}" 377 | ], 378 | "pattern": [ 379 | { 380 | "regexp": "^\\s+(FAIL)\\s+(.*)$", 381 | "code": 2 382 | }, 383 | { 384 | "regexp": "^$" 385 | }, 386 | { 387 | "regexp": "^\\s+(.*)$", 388 | "message": 1 389 | }, 390 | { 391 | "regexp": "^$" 392 | }, 393 | { 394 | "regexp": "^\\s+(.*): (.*):(\\d+):(\\d+)$", 395 | "file": 2, 396 | "line": 3, 397 | "column": 4, 398 | "loop": true 399 | } 400 | ] 401 | } 402 | ], 403 | "walkthroughs": [ 404 | { 405 | "id": "PSRule.getStarted", 406 | "title": "Get Started with PSRule", 407 | "description": "Learn about and start using PSRule with Visual Studio Code.", 408 | "steps": [ 409 | { 410 | "id": "configureOptions", 411 | "title": "Configure an options file", 412 | "description": "The options file **ps-rule.yaml** is used to initialize a workspace and configure features of PSRule.\n[Open an options file](command:PSRule.openOptionsFile)\n[Create an options file](command:PSRule.createOptionsFile)\nTip: [Close the side bar for more space](command:workbench.action.closeSidebar)", 413 | "media": { 414 | "markdown": "media/walkthroughs/getStarted/01_configureOptions.md" 415 | }, 416 | "completionEvents": [ 417 | "onCommand:PSRule.openOptionsFile", 418 | "onCommand:PSRule.createOptionsFile" 419 | ] 420 | }, 421 | { 422 | "id": "configureSettings", 423 | "title": "Configure settings", 424 | "description": "In addition to **ps-rule.yaml**, some settings that affect how PSRule runs within Visual Studio Code can be configured per user or per workspace.\n[Configure settings](command:PSRule.configureSettings)\nTip: __You can sync some of these settings across devices.__", 425 | "media": { 426 | "markdown": "media/walkthroughs/getStarted/02_configureSettings.md" 427 | }, 428 | "completionEvents": [ 429 | "onCommand:PSRule.configureSettings" 430 | ] 431 | }, 432 | { 433 | "id": "runTasks", 434 | "title": "Run analysis", 435 | "description": "Tasks can be used to run analysis on files within your current Visual Studio Code workspace.\n[Show tasks](command:PSRule.showTasks)\nOr you can run the [analysis](command:PSRule.runAnalysisTask) task directly from the command palette.", 436 | "media": { 437 | "markdown": "media/walkthroughs/getStarted/03_runTasks.md" 438 | }, 439 | "completionEvents": [ 440 | "command:PSRule.runAnalysisTask", 441 | "command:PSRule.showTasks" 442 | ] 443 | }, 444 | { 445 | "id": "learnMore", 446 | "title": "Learn more", 447 | "description": "Check out our [PSRule documentation](https://aka.ms/ps-rule) to learn more about features and customization.", 448 | "media": { 449 | "markdown": "media/walkthroughs/getStarted/04_learnMore.md" 450 | } 451 | } 452 | ], 453 | "featuredFor": [ 454 | "**/ps-rule.yaml", 455 | "**/*.Rule.yaml", 456 | "**/*.Rule.ps1", 457 | "**/*.Rule.jsonc" 458 | ] 459 | } 460 | ] 461 | }, 462 | "scripts": { 463 | "compile": "tsc -p ./", 464 | "watch": "tsc -watch -p ./", 465 | "pack": "vsce package --no-git-tag-version --no-update-package-json --readme-path extension.md --out out/package", 466 | "publish": "vsce publish", 467 | "lint": "eslint . --ext .ts,.tsx", 468 | "pretest": "npm run compile", 469 | "test": "node ./out/dist/test/runTest.js", 470 | "vscode:prepublish": "npm run -S esbuild-base -- --minify", 471 | "esbuild-base": "esbuild ./src/main.ts --bundle --outfile=out/dist/main.js --external:vscode --format=cjs --platform=node", 472 | "esbuild": "npm run -S esbuild-base -- --sourcemap", 473 | "esbuild-watch": "npm run -S esbuild-base -- --sourcemap --watch" 474 | }, 475 | "dependencies": { 476 | "vscode-languageclient": "^9.0.1", 477 | "fs-extra": "^11.3.0" 478 | }, 479 | "extensionDependencies": [ 480 | "vscode.powershell", 481 | "redhat.vscode-yaml" 482 | ], 483 | "devDependencies": { 484 | "@types/glob": "^8.1.0", 485 | "@types/mocha": "^10.0.10", 486 | "@types/node": "^22.15.21", 487 | "@types/vscode": "1.78.1", 488 | "@types/fs-extra": "^11.0.4", 489 | "@typescript-eslint/eslint-plugin": "^8.32.1", 490 | "@typescript-eslint/parser": "^8.32.1", 491 | "esbuild": "^0.25.2", 492 | "eslint": "^9.27.0", 493 | "glob": "^11.0.2", 494 | "lodash": ">=4.17.21", 495 | "markdown-it": "^14.1.0", 496 | "minimist": ">=1.2.8", 497 | "mocha": "^11.2.2", 498 | "typescript": "^5.8.3", 499 | "@vscode/vsce": "^3.4.2", 500 | "@vscode/test-electron": "^2.5.2", 501 | "nth-check": ">=2.1.1", 502 | "ansi-regex": ">=6.1.0" 503 | } 504 | } 505 | -------------------------------------------------------------------------------- /pipeline.build.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # Invoke-Build 5 | # CI pipeline script for PSRule-vscode 6 | 7 | [CmdletBinding()] 8 | param ( 9 | [Parameter(Mandatory = $False)] 10 | [String]$Build = '0.0.1', 11 | 12 | [Parameter(Mandatory = $False)] 13 | [ValidateSet('preview', 'stable', 'dev')] 14 | [String]$Channel, 15 | 16 | [Parameter(Mandatory = $False)] 17 | [String]$Configuration = 'Debug', 18 | 19 | [Parameter(Mandatory = $False)] 20 | [String]$OutputPath = (Join-Path -Path $PWD -ChildPath out), 21 | 22 | [Parameter(Mandatory = $False)] 23 | [String]$ApiKey, 24 | 25 | [Parameter(Mandatory = $False)] 26 | [String]$AssertStyle = 'AzurePipelines' 27 | ) 28 | 29 | $commitId = git log --format="%H" -n 1; 30 | 31 | Write-Host -Object "[Pipeline] -- PWD: $PWD" -ForegroundColor Green; 32 | Write-Host -Object "[Pipeline] -- OutputPath: $OutputPath" -ForegroundColor Green; 33 | Write-Host -Object "[Pipeline] -- BuildNumber: $($Env:BUILD_BUILDNUMBER)" -ForegroundColor Green; 34 | Write-Host -Object "[Pipeline] -- CommitId: $($commitId)" -ForegroundColor Green; 35 | Write-Host -Object "[Pipeline] -- SourceBranch: $($Env:BUILD_SOURCEBRANCH)" -ForegroundColor Green; 36 | Write-Host -Object "[Pipeline] -- SourceBranchName: $($Env:BUILD_SOURCEBRANCHNAME)" -ForegroundColor Green; 37 | 38 | if ($Env:SYSTEM_DEBUG -eq 'true') { 39 | $VerbosePreference = 'Continue'; 40 | } 41 | 42 | if ($Env:BUILD_SOURCEBRANCH -like '*/tags/*' -and $Env:BUILD_SOURCEBRANCHNAME -like 'v2.*') { 43 | $Build = $Env:BUILD_SOURCEBRANCHNAME.Substring(1); 44 | } 45 | 46 | $version = $Build; 47 | 48 | # Handle channel 49 | if ([String]::IsNullOrEmpty('Channel')) { 50 | $Channel = 'preview'; 51 | } 52 | $channelSuffix = '-preview'; 53 | $channelDisplayName = 'PSRule (Preview)'; 54 | switch ($Channel) { 55 | 'dev' { $channelSuffix = '-dev'; $channelDisplayName = 'PSRule V2 (Dev)'; } 56 | 'stable' { $channelSuffix = ''; $channelDisplayName = 'PSRule V2'; } 57 | default { $channelSuffix = '-preview'; $channelDisplayName = 'PSRule V2 (Preview)'; } 58 | } 59 | 60 | Write-Host -Object "[Pipeline] -- Using channel: $Channel" -ForegroundColor Green; 61 | Write-Host -Object "[Pipeline] -- Using channelSuffix: $channelSuffix" -ForegroundColor Green; 62 | Write-Host -Object "[Pipeline] -- Using version: $version" -ForegroundColor Green; 63 | 64 | $packageRoot = Join-Path -Path $OutputPath -ChildPath 'package'; 65 | $packageName = "psrule-vscode$channelSuffix"; 66 | $packagePath = Join-Path -Path $packageRoot -ChildPath "$packageName.vsix"; 67 | 68 | function Get-RepoRuleData { 69 | [CmdletBinding()] 70 | param ( 71 | [Parameter(Position = 0, Mandatory = $False)] 72 | [String]$Path = $PWD 73 | ) 74 | process { 75 | GetPathInfo -Path $Path -Verbose:$VerbosePreference; 76 | } 77 | } 78 | 79 | function GetPathInfo { 80 | [CmdletBinding()] 81 | param ( 82 | [Parameter(Mandatory = $True)] 83 | [String]$Path 84 | ) 85 | begin { 86 | $items = New-Object -TypeName System.Collections.ArrayList; 87 | } 88 | process { 89 | $Null = $items.Add((Get-Item -Path $Path)); 90 | $files = @(Get-ChildItem -Path $Path -File -Recurse -Include *.ps1,*.psm1,*.psd1,*.cs | Where-Object { 91 | !($_.FullName -like "*.Designer.cs") -and 92 | !($_.FullName -like "*/bin/*") -and 93 | !($_.FullName -like "*/obj/*") -and 94 | !($_.FullName -like "*\obj\*") -and 95 | !($_.FullName -like "*\bin\*") -and 96 | !($_.FullName -like "*\out\*") -and 97 | !($_.FullName -like "*/out/*") 98 | }); 99 | $Null = $items.AddRange($files); 100 | } 101 | end { 102 | $items; 103 | } 104 | } 105 | 106 | task BuildExtension { 107 | Write-Host '> Building extension' -ForegroundColor Green; 108 | exec { & npm run compile } 109 | } 110 | 111 | task PackageExtension { 112 | Write-Host '> Packaging PSRule-vscode' -ForegroundColor Green; 113 | if (!(Test-Path -Path $packageRoot)) { 114 | $Null = New-Item -Path $packageRoot -ItemType Directory -Force; 115 | } 116 | exec { & npm run pack -- $version } 117 | } 118 | 119 | # Synopsis: Install the extension in Visual Studio Code 120 | task InstallExtension { 121 | Write-Host '> Installing PSRule-vscode' -ForegroundColor Green; 122 | exec { & code --install-extension $packagePath --force } 123 | } 124 | 125 | task VersionExtension { 126 | # Update channel name 127 | $package = Get-Content ./package.json -Raw | ConvertFrom-Json; 128 | if ($package.name -ne $packageName) { 129 | $package.name = $packageName; 130 | $package | ConvertTo-Json -Depth 99 | Set-Content ./package.json; 131 | } 132 | 133 | # Update channel flag 134 | $package = Get-Content ./package.json -Raw | ConvertFrom-Json; 135 | $previewFlag = $Channel -ne 'stable'; 136 | if ($package.preview -ne $previewFlag) { 137 | $package.preview = $previewFlag; 138 | $package | ConvertTo-Json -Depth 99 | Set-Content ./package.json; 139 | } 140 | 141 | # Update channel display name 142 | $package = Get-Content ./package.json -Raw | ConvertFrom-Json; 143 | if ($package.displayName -ne $channelDisplayName) { 144 | $package.displayName = $channelDisplayName; 145 | $package | ConvertTo-Json -Depth 99 | Set-Content ./package.json; 146 | } 147 | 148 | if (![String]::IsNullOrEmpty($Build)) { 149 | # Update extension version 150 | if (![String]::IsNullOrEmpty($version)) { 151 | Write-Verbose -Message "[VersionExtension] -- Updating extension version"; 152 | $package = Get-Content ./package.json -Raw | ConvertFrom-Json; 153 | 154 | if ($package.version -ne $version) { 155 | $package.version = $version; 156 | $package | ConvertTo-Json -Depth 99 | Set-Content ./package.json; 157 | } 158 | } 159 | } 160 | } 161 | 162 | # Synopsis: Install NuGet provider 163 | task NuGet { 164 | if ($Null -eq (Get-PackageProvider -Name NuGet -ErrorAction Ignore)) { 165 | Install-PackageProvider -Name NuGet -Force -Scope CurrentUser; 166 | } 167 | } 168 | 169 | # Synopsis: Install PSRule 170 | task PSRule NuGet, { 171 | if ($Null -eq (Get-InstalledModule -Name PSRule -MinimumVersion 2.0.0 -ErrorAction Ignore)) { 172 | Install-Module -Name PSRule -Repository PSGallery -MinimumVersion 2.0.0 -Scope CurrentUser -Force; 173 | } 174 | Import-Module -Name PSRule -Verbose:$False; 175 | } 176 | 177 | # Synopsis: Run validation 178 | task Rules PSRule, { 179 | $assertParams = @{ 180 | Path = './.ps-rule/' 181 | Style = $AssertStyle 182 | OutputFormat = 'NUnit3' 183 | ErrorAction = 'Stop' 184 | As = 'Summary' 185 | } 186 | Assert-PSRule @assertParams -InputPath $PWD -Format File -OutputPath reports/ps-rule-file.xml; 187 | } 188 | 189 | # Synopsis: Remove temp files 190 | task Clean { 191 | Remove-Item -Path out,reports -Recurse -Force -ErrorAction Ignore; 192 | } 193 | 194 | # Synopsis: Restore NPM packages 195 | task PackageRestore { 196 | exec { & npm install --no-save } 197 | } 198 | 199 | task ReleaseExtension { 200 | exec { & npm install vsce --no-save } 201 | exec { & npm run publish -- --packagePath $packagePath --pat $ApiKey } 202 | } 203 | 204 | # Synopsis: Add shipit build tag 205 | task TagBuild { 206 | if ($Null -ne $Env:BUILD_DEFINITIONNAME) { 207 | Write-Host "`#`#vso[build.addbuildtag]shipit"; 208 | } 209 | } 210 | 211 | task Build Clean, PackageRestore, VersionExtension, PackageExtension 212 | 213 | task Install Build, InstallExtension 214 | 215 | task . Build 216 | 217 | task Release VersionExtension, ReleaseExtension, TagBuild 218 | -------------------------------------------------------------------------------- /ps-project.yaml: -------------------------------------------------------------------------------- 1 | 2 | info: 3 | name: PSRule 4 | description: | 5 | Visual Studio Code extension for PSRule. 6 | url: https://github.com/Microsoft/PSRule-vscode 7 | 8 | repository: 9 | type: git 10 | url: https://github.com/Microsoft/PSRule-vscode.git 11 | 12 | bugs: 13 | url: https://github.com/Microsoft/PSRule-vscode/issues 14 | 15 | tasks: 16 | clear: 17 | steps: 18 | - gitPrune: 19 | name: origin 20 | removeGone: true 21 | -------------------------------------------------------------------------------- /ps-rule.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # PSRule configuration 3 | # 4 | 5 | # Please see the documentation for all configuration options: 6 | # https://aka.ms/ps-rule/options 7 | 8 | repository: 9 | url: https://github.com/microsoft/PSRule-vscode 10 | 11 | requires: 12 | PSRule: '@pre >=2.2.0' 13 | PSRule.Rules.MSFT.OSS: '@pre >=1.0.1' 14 | 15 | include: 16 | module: 17 | - PSRule.Rules.MSFT.OSS 18 | 19 | output: 20 | culture: 21 | - en-US 22 | 23 | input: 24 | pathIgnore: 25 | - '*.md' 26 | - 'schemas/*.json' 27 | - 'syntaxes/*.json' 28 | - 'snippets/*.json' 29 | - '.vscode/' 30 | - '**/node_modules/' 31 | -------------------------------------------------------------------------------- /schemas/PSRule-resources.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft-07/schema#", 3 | "title": "PSRule resources", 4 | "description": "A meta schema for defining PSRule resources in a JSON document.", 5 | "type": "array", 6 | "items": { 7 | "anyOf": [ 8 | { 9 | "$ref": "https://raw.githubusercontent.com/microsoft/PSRule/main/schemas/PSRule-language.schema.json#/definitions/rule-v1" 10 | }, 11 | { 12 | "$ref": "https://raw.githubusercontent.com/microsoft/PSRule/main/schemas/PSRule-language.schema.json#/definitions/baseline-v1" 13 | }, 14 | { 15 | "$ref": "https://raw.githubusercontent.com/microsoft/PSRule/main/schemas/PSRule-language.schema.json#/definitions/moduleConfig-v1" 16 | }, 17 | { 18 | "$ref": "https://raw.githubusercontent.com/microsoft/PSRule/main/schemas/PSRule-language.schema.json#/definitions/selector-v1" 19 | }, 20 | { 21 | "$ref": "https://raw.githubusercontent.com/microsoft/PSRule/main/schemas/PSRule-language.schema.json#/definitions/suppressionGroup-v1" 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/schemas.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # Note: 5 | # Handles dependencies updates. 6 | 7 | function Update-Schemas { 8 | [CmdletBinding()] 9 | param ( 10 | [Parameter(Mandatory = $False)] 11 | [String]$Path = (Join-Path -Path $PWD -ChildPath 'PSRule/schemas/'), 12 | 13 | [Parameter(Mandatory = $False)] 14 | [String]$Target = (Join-Path -Path $PWD -ChildPath 'schemas/') 15 | ) 16 | process { 17 | $files = Get-ChildItem -Path $Path -File -Filter '*.schema.json'; 18 | $files | ForEach-Object { 19 | Copy-Item -Path $_.FullName -Destination $Target -Force; 20 | } 21 | 22 | if (!(Test-Path -Path 'out/')) { 23 | $Null = New-Item -Path 'out/' -ItemType Directory -Force; 24 | } 25 | 26 | $updates = @(git status --porcelain); 27 | if ($Null -ne $Env:WORKING_BRANCH -and $Null -ne $updates -and $updates.Length -gt 0) { 28 | git add schemas/*; 29 | git commit -m "Update schemas/"; 30 | git push --force -u origin $Env:WORKING_BRANCH; 31 | 32 | $updates | ForEach-Object { 33 | if ($_ -like '* schemas/*') { 34 | "Bump $($_.Substring(3))"; 35 | } 36 | } | Set-Content -Path 'out/updates.txt' -Force; 37 | 38 | $existingBranch = @(gh pr list --head $Env:WORKING_BRANCH --state open --json number | ConvertFrom-Json); 39 | if ($Null -eq $existingBranch -or $existingBranch.Length -eq 0) { 40 | gh pr create -B 'main' -H $Env:WORKING_BRANCH -l 'dependencies' -t 'Bump PSRule schemas' -F 'out/updates.txt'; 41 | } 42 | else { 43 | $pr = $existingBranch[0].number 44 | gh pr edit $pr -F 'out/updates.txt'; 45 | } 46 | } 47 | } 48 | } 49 | 50 | Export-ModuleMember -Function @( 51 | 'Update-Schemas' 52 | ) 53 | -------------------------------------------------------------------------------- /snippets/github-snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "PSRule in GitHub Actions workflow": { 3 | "prefix": "ps-rule-gh-action", 4 | "description": "PSRule workflow snippet for GitHub Actions.", 5 | "body": [ 6 | "#", 7 | "# Analyze repository with PSRule", 8 | "#", 9 | "", 10 | "# For PSRule documentation see:", 11 | "# https://aka.ms/ps-rule", 12 | "", 13 | "# For action details see:", 14 | "# https://aka.ms/ps-rule-action", 15 | "", 16 | "name: Analyze repository", 17 | "", 18 | "# Run for main or PRs against main", 19 | "on:", 20 | " push:", 21 | " branches:", 22 | " - main", 23 | " pull_request:", 24 | " branches:", 25 | " - main", 26 | "", 27 | "jobs:", 28 | " analyze:", 29 | " name: Analyze repository", 30 | " runs-on: ubuntu-latest", 31 | " steps:", 32 | "", 33 | " - name: Checkout", 34 | " uses: actions/checkout@v3", 35 | "", 36 | " - name: Run PSRule analysis", 37 | " uses: Microsoft/ps-rule@v2.8.1", 38 | " with:", 39 | " modules: ${1}" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /snippets/json.json: -------------------------------------------------------------------------------- 1 | { 2 | "Baseline Json": { 3 | "prefix": "baseline-json", 4 | "description": "PSRule Baseline JSON resource", 5 | "body": [ 6 | "{", 7 | " // Synopsis: ${2}", 8 | " \"apiVersion\": \"github.com/microsoft/PSRule/v1\",", 9 | " \"kind\": \"Baseline\",", 10 | " \"metadata\": {", 11 | " \"name\": \"${1}\"", 12 | " },", 13 | " \"spec\": {", 14 | " ${3}", 15 | " }", 16 | "}" 17 | ] 18 | }, 19 | "ModuleConfig Json": { 20 | "prefix": "moduleConfig-json", 21 | "description": "PSRule Module Config JSON resource", 22 | "body": [ 23 | "{", 24 | " // Synopsis: ${2}", 25 | " \"apiVersion\": \"github.com/microsoft/PSRule/v1\",", 26 | " \"kind\": \"ModuleConfig\",", 27 | " \"metadata\": {", 28 | " \"name\": \"${1}\"", 29 | " },", 30 | " \"spec\": {", 31 | " ${3}", 32 | " }", 33 | "}" 34 | ] 35 | }, 36 | "Selector Json": { 37 | "prefix": "selector-json", 38 | "description": "PSRule Selector JSON resource", 39 | "body": [ 40 | "{", 41 | " // Synopsis: ${2}", 42 | " \"apiVersion\": \"github.com/microsoft/PSRule/v1\",", 43 | " \"kind\": \"Selector\",", 44 | " \"metadata\": {", 45 | " \"name\": \"${1}\"", 46 | " },", 47 | " \"spec\": {", 48 | " \"if\": {", 49 | " ${3}", 50 | " }", 51 | " }", 52 | "}" 53 | ] 54 | }, 55 | "Rule with Type Json": { 56 | "prefix": "rule-with-type-json", 57 | "description": "PSRule Rule JSON resource with Type pre-condition", 58 | "body": [ 59 | "{", 60 | " // Synopsis: ${2}", 61 | " \"apiVersion\": \"github.com/microsoft/PSRule/v1\",", 62 | " \"kind\": \"Rule\",", 63 | " \"metadata\": {", 64 | " \"name\": \"${1}\"", 65 | " },", 66 | " \"spec\": {", 67 | " \"type\": [", 68 | " \"${3}\"", 69 | " ],", 70 | " \"condition\": {", 71 | " ${4}", 72 | " }", 73 | " }", 74 | "}" 75 | ] 76 | }, 77 | "Rule with Selector Json": { 78 | "prefix": "rule-with-selector-json", 79 | "description": "PSRule Rule JSON resource with Selector pre-condition", 80 | "body": [ 81 | "{", 82 | " // Synopsis: ${2}", 83 | " \"apiVersion\": \"github.com/microsoft/PSRule/v1\",", 84 | " \"kind\": \"Rule\",", 85 | " \"metadata\": {", 86 | " \"name\": \"${1}\"", 87 | " },", 88 | " \"spec\": {", 89 | " \"with\": [", 90 | " \"${3}\"", 91 | " ],", 92 | " \"condition\": {", 93 | " ${4}", 94 | " }", 95 | " }", 96 | "}" 97 | ] 98 | }, 99 | "Suppression Group Json": { 100 | "prefix": "suppression-group-json", 101 | "description": "PSRule Suppression Group JSON resource", 102 | "body": [ 103 | "{", 104 | " // Synopsis: ${2}", 105 | " \"apiVersion\": \"github.com/microsoft/PSRule/v1\",", 106 | " \"kind\": \"SuppressionGroup\",", 107 | " \"metadata\": {", 108 | " \"name\": \"${1}\"", 109 | " },", 110 | " \"spec\": {", 111 | " \"rule\": [", 112 | " \"${3}\"", 113 | " ],", 114 | " \"if\": {", 115 | " ${4}", 116 | " }", 117 | " }", 118 | "}" 119 | ] 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /snippets/markdown.json: -------------------------------------------------------------------------------- 1 | { 2 | "Rule Doc": { 3 | "prefix": "rule", 4 | "description": "Rule documentation", 5 | "body": [ 6 | "# ${1:Name of rule}", 7 | "", 8 | "## SYNOPSIS", 9 | "", 10 | "{{ A brief summary of the rule }}", 11 | "", 12 | "## DESCRIPTION", 13 | "", 14 | "{{ A detailed description of the rule }}", 15 | "", 16 | "## RECOMMENDATION", 17 | "", 18 | "{{ A detailed explanation of the steps required to pass the rule }}", 19 | "" 20 | ] 21 | }, 22 | "Rule Doc Extended": { 23 | "prefix": "rule", 24 | "description": "Extended rule documentation", 25 | "body": [ 26 | "---", 27 | "online version:", 28 | "---", 29 | "", 30 | "# ${1:Name of rule}", 31 | "", 32 | "## SYNOPSIS", 33 | "", 34 | "{{ A brief summary of the rule }}", 35 | "", 36 | "## DESCRIPTION", 37 | "", 38 | "{{ A detailed description of the rule }}", 39 | "", 40 | "## RECOMMENDATION", 41 | "", 42 | "{{ A detailed explanation of the steps required to pass the rule }}", 43 | "", 44 | "## NOTES", 45 | "", 46 | "{{ Additional information or configuration options }}", 47 | "", 48 | "## LINKS", 49 | "", 50 | "{{ Links to external references }}", 51 | "" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /snippets/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "PSRule options": { 3 | "prefix": "ps-rule-options", 4 | "description": "Default PSRule options file", 5 | "body": [ 6 | "#", 7 | "# PSRule configuration", 8 | "#", 9 | "", 10 | "# Please see the documentation for all configuration options:", 11 | "# https://aka.ms/ps-rule/options", 12 | "", 13 | "requires:", 14 | " PSRule: '@pre >=2.8.1'", 15 | "", 16 | "output:", 17 | " culture:", 18 | " - en-US", 19 | "", 20 | "input:", 21 | " pathIgnore:", 22 | " - '*.md'", 23 | " - '.vscode/'", 24 | " - 'docs/'", 25 | "" 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /snippets/pipelines-snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "PSRule in Azure Pipelines": { 3 | "prefix": "ps-rule-az-pipeline", 4 | "description": "PSRule snippet for Azure Pipelines.", 5 | "body": [ 6 | "#", 7 | "# Analyze repository with PSRule", 8 | "#", 9 | "", 10 | "# For PSRule documentation see:", 11 | "# https://aka.ms/ps-rule", 12 | "", 13 | "# For pipeline task details see:", 14 | "# https://aka.ms/ps-rule-pipelines", 15 | "", 16 | "# Run for main or PRs against main", 17 | "trigger:", 18 | " branches:", 19 | " include:", 20 | " - main", 21 | "", 22 | "pr:", 23 | " branches:", 24 | " include:", 25 | " - main", 26 | "", 27 | "stages:", 28 | "- stage: Test", 29 | " jobs:", 30 | " - job: analyze", 31 | " displayName: Analyze repository", 32 | " pool:", 33 | " vmImage: ubuntu-latest", 34 | " steps:", 35 | "", 36 | " - task: ps-rule-assert@2", 37 | " displayName: Run PSRule analysis", 38 | " inputs:", 39 | " modules: ${1}", 40 | "" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /snippets/powershell.json: -------------------------------------------------------------------------------- 1 | { 2 | "rule": { 3 | "prefix": "rule", 4 | "description": "Rule definition", 5 | "body": [ 6 | "# Synopsis: ${2}", 7 | "Rule '${1}' {", 8 | "\t$0", 9 | "}" 10 | ] 11 | }, 12 | "ruleIf": { 13 | "prefix": "rule-if", 14 | "description": "Rule definition with script precondition", 15 | "body": [ 16 | "# Synopsis: ${2}", 17 | "Rule '${1}' -If { ${3} } {", 18 | "\t$0", 19 | "}" 20 | ] 21 | }, 22 | "ruleType": { 23 | "prefix": "rule-type", 24 | "description": "Rule definition with type precondition", 25 | "body": [ 26 | "# Synopsis: ${2}", 27 | "Rule '${1}' -Type '${3}' {", 28 | "\t$0", 29 | "}" 30 | ] 31 | }, 32 | "ruleExtended": { 33 | "prefix": "rule-extended", 34 | "description": "Rule definition with script precondition and tags", 35 | "body": [ 36 | "# Synopsis: ${2}", 37 | "Rule '${1}' -If { ${3} } -Tag @{ ${4} } {", 38 | "\t$0", 39 | "}" 40 | ] 41 | }, 42 | "anyOf": { 43 | "prefix": "anyOf", 44 | "description": "AnyOf condition block", 45 | "body": [ 46 | "AnyOf {", 47 | "\t$0", 48 | "}" 49 | ] 50 | }, 51 | "allOf": { 52 | "prefix": "allOf", 53 | "description": "AllOf condition block", 54 | "body": [ 55 | "AllOf {", 56 | "\t$0", 57 | "}" 58 | ] 59 | }, 60 | "exists": { 61 | "prefix": "exists", 62 | "description": "Exists condition", 63 | "body": [ 64 | "Exists '$0'" 65 | ] 66 | }, 67 | "within": { 68 | "prefix": "within", 69 | "description": "Within condition", 70 | "body": [ 71 | "Within '$1' '$0'" 72 | ] 73 | }, 74 | "typeOf": { 75 | "prefix": "typeOf", 76 | "description": "TypeOf condition", 77 | "body": [ 78 | "TypeOf '$0'" 79 | ] 80 | }, 81 | "recommend": { 82 | "prefix": "recommend", 83 | "description": "Recommend message", 84 | "body": [ 85 | "Recommend '$0'" 86 | ] 87 | }, 88 | "reason": { 89 | "prefix": "reason", 90 | "description": "Reason message", 91 | "body": [ 92 | "Reason '$0'" 93 | ] 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /snippets/yaml.json: -------------------------------------------------------------------------------- 1 | { 2 | "Baseline Yaml": { 3 | "prefix": "baseline-yaml", 4 | "description": "PSRule Baseline YAML resource", 5 | "body": [ 6 | "---", 7 | "# Synopsis: ${2}", 8 | "apiVersion: github.com/microsoft/PSRule/v1", 9 | "kind: Baseline", 10 | "metadata:", 11 | " name: ${1}", 12 | "spec:", 13 | " ${3}" 14 | ] 15 | }, 16 | "ModuleConfig Yaml": { 17 | "prefix": "moduleConfig-yaml", 18 | "description": "PSRule Module Config YAML resource", 19 | "body": [ 20 | "---", 21 | "# Synopsis: ${2}", 22 | "apiVersion: github.com/microsoft/PSRule/v1", 23 | "kind: ModuleConfig", 24 | "metadata:", 25 | " name: ${1}", 26 | "spec:", 27 | " ${3}" 28 | ] 29 | }, 30 | "Selector Yaml": { 31 | "prefix": "selector-yaml", 32 | "description": "PSRule Selector YAML resource", 33 | "body": [ 34 | "---", 35 | "# Synopsis: ${2}", 36 | "apiVersion: github.com/microsoft/PSRule/v1", 37 | "kind: Selector", 38 | "metadata:", 39 | " name: ${1}", 40 | "spec:", 41 | " if:", 42 | " ${3}" 43 | ] 44 | }, 45 | "Rule with Type Yaml": { 46 | "prefix": "rule-with-type-yaml", 47 | "description": "PSRule Rule YAML resource with Type pre-condition", 48 | "body": [ 49 | "---", 50 | "# Synopsis: ${2}", 51 | "apiVersion: github.com/microsoft/PSRule/v1", 52 | "kind: Rule", 53 | "metadata:", 54 | " name: ${1}", 55 | "spec:", 56 | " type:", 57 | " - ${3}", 58 | " condition:", 59 | " ${4}" 60 | ] 61 | }, 62 | "Rule with Selector Yaml": { 63 | "prefix": "rule-with-selector-yaml", 64 | "description": "PSRule Rule YAML resource with Selector pre-condition", 65 | "body": [ 66 | "---", 67 | "# Synopsis: ${2}", 68 | "apiVersion: github.com/microsoft/PSRule/v1", 69 | "kind: Rule", 70 | "metadata:", 71 | " name: ${1}", 72 | "spec:", 73 | " with:", 74 | " - ${3}", 75 | " condition:", 76 | " ${4}" 77 | ] 78 | }, 79 | "Suppression Group Yaml": { 80 | "prefix": "suppression-group-yaml", 81 | "description": "PSRule Suppression Group YAML resource", 82 | "body": [ 83 | "---", 84 | "# Synopsis: ${2}", 85 | "apiVersion: github.com/microsoft/PSRule/v1", 86 | "kind: SuppressionGroup", 87 | "metadata:", 88 | " name: ${1}", 89 | "spec:", 90 | " rule: ", 91 | " - ${3}", 92 | " if:", 93 | " ${4}" 94 | ] 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/commands/configureSettings.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { commands } from 'vscode'; 5 | import { logger } from '../logger'; 6 | import { ext } from '../extension'; 7 | 8 | /** 9 | * Open settings automatically filtered to PSRule specific settings. 10 | * @returns A promise for the task. 11 | */ 12 | export async function configureSettings(): Promise { 13 | const extensionId = (await ext.info).id 14 | logger.verbose(`Opening settings for ${extensionId}`); 15 | commands.executeCommand('workbench.action.openSettings', `@ext:${extensionId}`); 16 | } 17 | -------------------------------------------------------------------------------- /src/commands/createOptionsFile.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as filepath from 'path'; 5 | import * as fse from 'fs-extra'; 6 | import { Position, TextDocument, Uri, window, workspace } from 'vscode'; 7 | import { logger } from '../logger'; 8 | import { getActiveOrFirstWorkspace, readOptionsSnippet } from '../utils'; 9 | 10 | /** 11 | * Create a new options file. 12 | * @param path The path to the options file. 13 | * @returns A promise for the task. 14 | */ 15 | export async function createOptionsFile(path: string | undefined): Promise { 16 | let defaultUri: Uri | undefined = getActiveOrFirstWorkspace()?.uri; 17 | if (defaultUri) { 18 | defaultUri = Uri.joinPath(defaultUri, '/ps-rule.yaml'); 19 | } 20 | if (path === '' || path === undefined) { 21 | const response = await window.showSaveDialog({ 22 | defaultUri: defaultUri, 23 | filters: { 'PSRule options file': ['yaml'] }, 24 | title: 'Where would you like to save the PSRule options file?', 25 | saveLabel: 'Save options file', 26 | }); 27 | if (!response || !response.fsPath) return; 28 | path = response.fsPath; 29 | } 30 | if (path === '' || path === undefined) return; 31 | 32 | let uri = Uri.file(path); 33 | if (uri) { 34 | let parent = Uri.file(filepath.dirname(uri.fsPath)); 35 | 36 | logger.verbose(`Using options path ${uri.fsPath}`); 37 | 38 | let exists = await fse.pathExists(uri.fsPath); 39 | if (!exists) { 40 | await fse.ensureDir(parent.fsPath); 41 | await fse.writeFile(uri.fsPath, '', { encoding: 'utf-8' }); 42 | } 43 | const document: TextDocument = await workspace.openTextDocument(uri); 44 | const editor = await window.showTextDocument(document); 45 | 46 | // Populate new options snippet 47 | if (!exists) { 48 | let snippet = await readOptionsSnippet('PSRule options'); 49 | if (snippet) { 50 | editor.insertSnippet(snippet, new Position(0, 0)); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/commands/createOrEditDocumentation.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as path from 'path'; 5 | import * as fse from 'fs-extra'; 6 | import { Position, TextDocument, Uri, window, workspace } from 'vscode'; 7 | import { logger } from '../logger'; 8 | import { getDocumentationPath, readDocumentationSnippet } from '../utils'; 9 | import { configuration } from '../configuration'; 10 | 11 | const validNameExpression = 12 | /[^<>:/\\|?*"'`+@._\-\x00-\x1F][^<>:/\\|?*"'`+@\x00-\x1F]{1,126}[^<>:/\\|?*"'`+@._\-\x00-\x1F]+/g; 13 | 14 | /** 15 | * Create or edit documentation for a rule. 16 | * @param name The name of the rule. 17 | * @returns A promise for the task. 18 | */ 19 | export async function createOrEditDocumentation(name: string | undefined): Promise { 20 | if (name === '' || name === undefined) { 21 | name = await window.showInputBox({ 22 | prompt: 'Enter the name of the rule to create documentation for.', 23 | validateInput: (value: string) => { 24 | return validNameExpression.test(value) ? undefined : 'Must be a valid rule name.'; 25 | }, 26 | }); 27 | } 28 | if (name === '' || name === undefined) return; 29 | 30 | let uri = await getDocumentationPath(name); 31 | 32 | if (uri) { 33 | let parent = Uri.file(path.dirname(uri.fsPath)); 34 | 35 | logger.verbose(`Using documentation path ${uri.fsPath}`); 36 | 37 | let exists = await fse.pathExists(uri.fsPath); 38 | if (!exists) { 39 | await fse.ensureDir(parent.fsPath); 40 | await fse.writeFile(uri.fsPath, '', { encoding: 'utf-8' }); 41 | } 42 | const document: TextDocument = await workspace.openTextDocument(uri); 43 | const editor = await window.showTextDocument(document); 44 | 45 | // Populate new documentation with a snippet 46 | const snippetConfig = configuration.get().documentationSnippet; 47 | const snippetPathConfig = configuration.get().documentationCustomSnippetPath; 48 | if (!exists && snippetConfig !== '') { 49 | let snippet = await readDocumentationSnippet(snippetPathConfig, snippetConfig); 50 | if (snippet) { 51 | editor.insertSnippet(snippet, new Position(0, 0)); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/commands/openOptionsFile.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as fse from 'fs-extra'; 5 | import { RelativePattern, TextDocument, Uri, window, workspace, WorkspaceFolder } from 'vscode'; 6 | import { logger } from '../logger'; 7 | import { getActiveOrFirstWorkspace } from '../utils'; 8 | 9 | /** 10 | * Open an existing options file. 11 | * @param path The path to the options file. 12 | * @returns A promise for the task. 13 | */ 14 | export async function openOptionsFile(path: string | undefined): Promise { 15 | const optionFilePath = await getOptionFile(path); 16 | if (optionFilePath === '' || optionFilePath === undefined) return; 17 | 18 | const uri = Uri.file(optionFilePath); 19 | logger.verbose(`Using options path ${uri.fsPath}`); 20 | const exists = await fse.pathExists(uri.fsPath); 21 | if (!exists) 22 | return; 23 | 24 | const document: TextDocument = await workspace.openTextDocument(uri); 25 | await window.showTextDocument(document); 26 | } 27 | 28 | async function getOptionFile(path: string | undefined): Promise { 29 | // Require an active workspace. 30 | const active: WorkspaceFolder | undefined = getActiveOrFirstWorkspace(); 31 | if (!active) return Promise.resolve(undefined); 32 | if (!(path === '' || path === undefined)) return Promise.resolve(path); 33 | 34 | const workspaceUri: Uri = active.uri; 35 | const searchPattern = new RelativePattern(workspaceUri, '**/ps-rule.yaml'); 36 | return new Promise((resolve) => { 37 | workspace.findFiles(searchPattern).then(files => { 38 | if (files === undefined || files.length === 0) 39 | resolve(undefined); 40 | 41 | const names: string[] = []; 42 | files.forEach(item => { 43 | names.push(item.path); 44 | }); 45 | window.showQuickPick(names, { title: 'Options file' }).then(item => { 46 | return resolve(item); 47 | }); 48 | }); 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /src/commands/runAnalysisTask.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { Task, tasks } from 'vscode'; 5 | import { getActiveOrFirstWorkspace } from '../utils'; 6 | import { ext } from '../extension'; 7 | 8 | /** 9 | * Runs the PSRule analysis task. 10 | * @returns A promise for the task. 11 | */ 12 | export async function runAnalysisTask(): Promise { 13 | const workspace = getActiveOrFirstWorkspace(); 14 | if (!workspace) return; 15 | const t = await ext.tasks?.getWorkspaceTasks(workspace); 16 | 17 | if (!t) return; 18 | 19 | const result: Task[] = []; 20 | t.forEach(task => { 21 | if (task.name === 'Run analysis' && task.source === 'PSRule') { 22 | result.push(task); 23 | } 24 | }); 25 | await tasks.executeTask(result[0]); 26 | } 27 | -------------------------------------------------------------------------------- /src/commands/showTasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import { commands } from 'vscode'; 5 | 6 | /** 7 | * Shows the quick pick list of available tasks. 8 | * @returns A promise for the task. 9 | */ 10 | export async function showTasks(): Promise { 11 | commands.executeCommand('workbench.action.tasks.runTask'); 12 | } 13 | -------------------------------------------------------------------------------- /src/commands/walkthroughCopySnippet.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as path from 'path'; 5 | import * as fse from 'fs-extra'; 6 | import { env } from 'vscode'; 7 | import { logger } from '../logger'; 8 | import { ext } from '../extension'; 9 | 10 | /** 11 | * Copy a walkthrough snippet to the clipboard by name. 12 | * @param name The name of a snippet to copy. 13 | * @returns A promise for the task. 14 | */ 15 | export async function walkthroughCopySnippet(name: string | undefined): Promise { 16 | // Clear the clipboard and load snippets. 17 | env.clipboard.writeText(''); 18 | const snippets = load('getStarted'); 19 | 20 | // Find the correct snippet and copy it to the clipboard. 21 | snippets.then((value): void => { 22 | value.forEach((element: { name: string; snippet: string[] | undefined; }) => { 23 | if (name === element.name && element.snippet) { 24 | logger.verbose(`Copying snippet for ${name} to the clipboard.`); 25 | const text = element.snippet.join('\n'); 26 | env.clipboard.writeText(text); 27 | return; 28 | } 29 | }); 30 | }); 31 | } 32 | 33 | /** 34 | * Load a snippet for a walkthrough. 35 | * @param name The name of the walkthrough. 36 | * @returns A list of named snippets for the walkthrough. 37 | */ 38 | async function load(name: string): Promise { 39 | const info = await ext.info; 40 | const helpFile = info ? path.join(info.path, `media/walkthroughs/${name}/snippets.json`) : undefined; 41 | if (helpFile && (await fse.pathExists(helpFile))) { 42 | return await fse.readJson(helpFile, { encoding: 'utf-8' }); 43 | } 44 | return Promise.resolve(undefined); 45 | } 46 | -------------------------------------------------------------------------------- /src/configuration.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | 'use strict'; 5 | 6 | import { ConfigurationChangeEvent, ExtensionContext, env, workspace } from 'vscode'; 7 | import { configurationItemPrefix } from './consts'; 8 | 9 | /** 10 | * The output of analysis tasks. 11 | */ 12 | export enum OutputAs { 13 | Detail = 'Detail', 14 | Summary = 'Summary', 15 | } 16 | 17 | export enum ExecutionActionPreference { 18 | /** 19 | * No preference. 20 | * This will inherit from the default. 21 | */ 22 | None = 'None', 23 | 24 | /** 25 | * Continue to execute silently. 26 | */ 27 | Ignore = 'Ignore', 28 | /** 29 | * Continue to execute but log a warning. 30 | */ 31 | Warn = 'Warn', 32 | 33 | /** 34 | * Generate an error. 35 | */ 36 | Error = 'Error', 37 | } 38 | 39 | /** 40 | * PSRule extension settings. 41 | */ 42 | export interface ISetting { 43 | codeLensRuleDocumentationLinks: boolean; 44 | documentationCustomSnippetPath: string | undefined; 45 | documentationSnippet: string; 46 | documentationPath: string | undefined; 47 | documentationLocalePath: string; 48 | 49 | /** 50 | * Execution options 51 | */ 52 | executionNotProcessedWarning: boolean | undefined; 53 | executionRuleExcluded: ExecutionActionPreference; 54 | executionRuleSuppressed: ExecutionActionPreference; 55 | executionUnprocessedObject: ExecutionActionPreference; 56 | 57 | /** 58 | * Determines if experimental features are enabled. 59 | */ 60 | experimentalEnabled: boolean; 61 | outputAs: OutputAs; 62 | notificationsShowChannelUpgrade: boolean; 63 | notificationsShowPowerShellExtension: boolean; 64 | 65 | /** 66 | * The name of the default baseline to use for executing rules. 67 | */ 68 | ruleBaseline: string | undefined; 69 | } 70 | 71 | /** 72 | * Default configuration for PSRule extension settings. 73 | */ 74 | const globalDefaults: ISetting = { 75 | codeLensRuleDocumentationLinks: true, 76 | documentationCustomSnippetPath: undefined, 77 | documentationSnippet: 'Rule Doc', 78 | documentationPath: undefined, 79 | documentationLocalePath: env.language, 80 | executionNotProcessedWarning: undefined, 81 | executionRuleExcluded: ExecutionActionPreference.None, 82 | executionRuleSuppressed: ExecutionActionPreference.None, 83 | executionUnprocessedObject: ExecutionActionPreference.None, 84 | experimentalEnabled: false, 85 | outputAs: OutputAs.Summary, 86 | notificationsShowChannelUpgrade: true, 87 | notificationsShowPowerShellExtension: true, 88 | ruleBaseline: undefined, 89 | }; 90 | 91 | /** 92 | * A configuration manager class for PSRule. 93 | */ 94 | export class ConfigurationManager { 95 | private current: ISetting; 96 | private readonly default: ISetting; 97 | private readonly configurationItemPrefix: string; 98 | 99 | /** 100 | * A flag for when setting require reload. 101 | */ 102 | private pendingLoad: boolean = true; 103 | 104 | constructor(setting?: ISetting, prefix?: string) { 105 | this.configurationItemPrefix = prefix ?? configurationItemPrefix; 106 | this.default = setting ?? globalDefaults; 107 | this.current = { ...this.default }; 108 | this.loadSettings(); 109 | } 110 | 111 | static configure(context: ExtensionContext) { 112 | if (context) { 113 | context.subscriptions.push( 114 | workspace.onDidChangeConfiguration( 115 | configuration.onConfigurationChanged, 116 | configuration 117 | ) 118 | ); 119 | } 120 | } 121 | 122 | public get(): ISetting { 123 | if (this.pendingLoad) { 124 | this.loadSettings(); 125 | } 126 | return this.current; 127 | } 128 | 129 | private onConfigurationChanged(e: ConfigurationChangeEvent) { 130 | if (!e.affectsConfiguration(this.configurationItemPrefix)) { 131 | return; 132 | } 133 | this.pendingLoad = true; 134 | } 135 | 136 | private loadSettings(): void { 137 | const config = workspace.getConfiguration(this.configurationItemPrefix); 138 | 139 | // Experimental 140 | let experimental = (this.current.experimentalEnabled = config.get( 141 | 'experimental.enabled', 142 | this.default.experimentalEnabled 143 | )); 144 | 145 | // Read settings 146 | this.current.documentationCustomSnippetPath = 147 | config.get('documentation.customSnippetPath') ?? 148 | this.default.documentationCustomSnippetPath; 149 | 150 | this.current.documentationSnippet = 151 | config.get('documentation.snippet') ?? this.default.documentationSnippet; 152 | 153 | this.current.documentationPath = 154 | config.get('documentation.path') ?? this.default.documentationPath; 155 | 156 | this.current.documentationLocalePath = 157 | config.get('documentation.localePath') ?? this.default.documentationLocalePath; 158 | 159 | this.current.codeLensRuleDocumentationLinks = config.get( 160 | 'codeLens.ruleDocumentationLinks', 161 | this.default.codeLensRuleDocumentationLinks 162 | ); 163 | 164 | this.current.executionNotProcessedWarning = config.get('execution.notProcessedWarning'); 165 | this.current.executionRuleExcluded = config.get('execution.ruleExcluded', this.default.executionRuleExcluded); 166 | this.current.executionRuleSuppressed = config.get('execution.ruleSuppressed', this.default.executionRuleSuppressed); 167 | this.current.executionUnprocessedObject = config.get('execution.unprocessedObject', this.default.executionUnprocessedObject); 168 | 169 | this.current.outputAs = config.get('output.as', this.default.outputAs); 170 | 171 | this.current.notificationsShowChannelUpgrade = config.get( 172 | 'notifications.showChannelUpgrade', 173 | this.default.notificationsShowChannelUpgrade 174 | ); 175 | 176 | this.current.notificationsShowPowerShellExtension = config.get( 177 | 'notifications.showPowerShellExtension', 178 | this.default.notificationsShowPowerShellExtension 179 | ); 180 | 181 | this.current.ruleBaseline = 182 | config.get('rule.baseline') ?? this.default.ruleBaseline; 183 | 184 | // Clear dirty settings flag 185 | this.pendingLoad = false; 186 | } 187 | } 188 | 189 | export const configuration = new ConfigurationManager(); 190 | -------------------------------------------------------------------------------- /src/consts.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | 'use strict'; 5 | 6 | export const defaultOptionsFile: string = 'ps-rule.yaml'; 7 | export const configurationItemPrefix: string = 'PSRule'; 8 | -------------------------------------------------------------------------------- /src/docLens.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | 'use strict'; 5 | 6 | import * as fse from 'fs-extra'; 7 | import { 8 | CancellationToken, 9 | CodeLens, 10 | CodeLensProvider, 11 | Command, 12 | Disposable, 13 | Event, 14 | EventEmitter, 15 | ExtensionContext, 16 | Position, 17 | Range, 18 | TextDocument, 19 | commands, 20 | languages, 21 | workspace, 22 | } from 'vscode'; 23 | import { DocumentSelector } from 'vscode-languageclient'; 24 | import { createOrEditDocumentation } from './commands/createOrEditDocumentation'; 25 | import { configuration } from './configuration'; 26 | import { ILogger } from './logger'; 27 | import { getDocumentationPath } from './utils'; 28 | 29 | interface IContext { 30 | readonly logger: ILogger; 31 | readonly extensionContext: ExtensionContext; 32 | } 33 | 34 | export class DocumentationLensProvider implements CodeLensProvider, Disposable { 35 | // Fields 36 | private readonly context: IContext; 37 | 38 | private codeLenses: CodeLens[] = []; 39 | private regexPowerShell: RegExp; 40 | private regexYaml: RegExp; 41 | private regexJson: RegExp; 42 | private _registration: Disposable | undefined; 43 | private _onDidChangeCodeLenses: EventEmitter = new EventEmitter(); 44 | public readonly onDidChangeCodeLenses: Event = this._onDidChangeCodeLenses.event; 45 | 46 | constructor(logger: ILogger, extensionContext: ExtensionContext) { 47 | this.context = { logger, extensionContext }; 48 | this.regexPowerShell = 49 | /(?Rule )(?:-Name\s)?(?['"]?[^<>:/\\|?*"'`+@._\-\x00-\x1F][^<>:/\\|?*"'`+@\x00-\x1F]{1,126}[^<>:/\\|?*"'`+@._\-\x00-\x1F]+['"]?)(?:.+)/gi; 50 | this.regexYaml = 51 | /(?apiVersion: github\.com\/microsoft\/PSRule\/v1)(?:\s+kind: Rule\s+metadata:\s+)(?: name: ?)(?['"]?[^<>:/\\|?*"'`+@._\-\x00-\x1F][^<>:/\\|?*"'`+@\x00-\x1F]{1,126}[^<>:/\\|?*"'`+@._\-\x00-\x1F]+['"]?)/gi; 52 | this.regexJson = 53 | /("apiVersion":\s*"github\.com\/microsoft\/PSRule\/v1",)(?:\s*"kind":\s*"Rule",\s*"metadata":\s*{\s*)(?:"name":\s)(?"[^<>:/\\|?*"'`+@._\-\x00-\x1F][^<>:/\\|?*"'`+@\x00-\x1F]{1,126}[^<>:/\\|?*"'`+@._\-\x00-\x1F]+")/gi; 54 | 55 | workspace.onDidChangeConfiguration((_) => { 56 | this._onDidChangeCodeLenses.fire(); 57 | }); 58 | } 59 | 60 | dispose() { 61 | this._registration?.dispose(); 62 | } 63 | 64 | public register(): void { 65 | let filter: DocumentSelector = [ 66 | { language: 'yaml', pattern: '**/*.Rule.yaml' }, 67 | { language: 'json', pattern: '**/*.Rule.json' }, 68 | { language: 'jsonc', pattern: '**/*.Rule.jsonc' }, 69 | { language: 'powershell', pattern: '**/*.Rule.ps1' }, 70 | ]; 71 | this._registration = languages.registerCodeLensProvider(filter, this); 72 | commands.registerCommand('PSRule.createOrEditDocumentation', (name: string) => { 73 | createOrEditDocumentation(name); 74 | }); 75 | } 76 | 77 | public async provideCodeLenses( 78 | document: TextDocument, 79 | token: CancellationToken 80 | ): Promise { 81 | if (configuration.get().codeLensRuleDocumentationLinks) { 82 | const regex = this.getLanguageExpression(document.languageId); 83 | if (regex === undefined) return []; 84 | 85 | this.codeLenses = []; 86 | const text = document.getText(); 87 | let matches; 88 | while ((matches = regex.exec(text)) !== null) { 89 | let name = 90 | matches.groups !== undefined ? matches.groups['name'].replace(/\'/g, '') : ''; 91 | const line = document.lineAt(document.positionAt(matches.index).line); 92 | const indexOf = line.text.indexOf(matches[1]); 93 | const position = new Position(line.lineNumber, indexOf); 94 | const range = document.getWordRangeAtPosition(position); 95 | if (range && name) { 96 | this.codeLenses.push(await this.createCodeLens(range, name)); 97 | } 98 | } 99 | return this.codeLenses; 100 | } 101 | return []; 102 | } 103 | 104 | private getLanguageExpression(language: string): RegExp | undefined { 105 | if (language === 'powershell') return this.regexPowerShell; 106 | if (language === 'yaml') return this.regexYaml; 107 | if (language === 'json' || language === 'jsonc') return this.regexJson; 108 | return undefined; 109 | } 110 | 111 | /** 112 | * Create a rule code lens. 113 | * @param range The range in the document the code lens applies to. 114 | * @param name The name of the rule. 115 | * @returns A code lens object. 116 | */ 117 | private async createCodeLens(range: Range, name: string): Promise { 118 | let uri = await getDocumentationPath(name); 119 | let exists = uri !== undefined && (await fse.pathExists(uri.fsPath)); 120 | let title = exists ? 'Open documentation' : 'Create documentation'; 121 | let tooltip = exists ? 'Open documentation for rule' : 'Create documentation for rule'; 122 | 123 | return new CodeLens(range, { 124 | title: title, 125 | tooltip: tooltip, 126 | command: 'PSRule.createOrEditDocumentation', 127 | arguments: [name], 128 | }); 129 | } 130 | 131 | public async resolveCodeLens( 132 | codeLens: CodeLens, 133 | token: CancellationToken 134 | ): Promise { 135 | return undefined; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | 'use strict'; 5 | 6 | import * as path from 'path'; 7 | import * as vscode from 'vscode'; 8 | import { logger } from './logger'; 9 | import { PSRuleTaskProvider } from './tasks'; 10 | import { ConfigurationManager } from './configuration'; 11 | import { pwsh } from './powershell'; 12 | import { DocumentationLensProvider } from './docLens'; 13 | import { createOptionsFile } from './commands/createOptionsFile'; 14 | import { openOptionsFile } from './commands/openOptionsFile'; 15 | import { walkthroughCopySnippet } from './commands/walkthroughCopySnippet'; 16 | import { configureSettings } from './commands/configureSettings'; 17 | import { runAnalysisTask } from './commands/runAnalysisTask'; 18 | import { showTasks } from './commands/showTasks'; 19 | 20 | export let taskManager: PSRuleTaskProvider | undefined; 21 | export let docLensProvider: DocumentationLensProvider | undefined; 22 | 23 | export interface ExtensionInfo { 24 | id: string; 25 | version: string; 26 | channel: string; 27 | path: string; 28 | disable: boolean; 29 | } 30 | 31 | export class ExtensionManager implements vscode.Disposable { 32 | private _info!: ExtensionInfo; 33 | private _context!: vscode.ExtensionContext; 34 | 35 | constructor() { } 36 | 37 | /** 38 | * Information about the extension. 39 | */ 40 | public get info(): Promise { 41 | const parent = this; 42 | return new Promise((resolve, reject) => { 43 | if (parent._info) { 44 | resolve(parent._info); 45 | } else { 46 | setTimeout(function (): void { 47 | if (parent._info) { 48 | resolve(parent._info); 49 | } else { 50 | reject('Failed to get info.'); 51 | } 52 | }, 1000); 53 | } 54 | }); 55 | } 56 | 57 | public get isTrusted(): boolean { 58 | return vscode.workspace.isTrusted; 59 | } 60 | 61 | /** 62 | * A task provider if the workspace is trusted, otherwise returns undefined. 63 | */ 64 | public get tasks(): PSRuleTaskProvider | undefined { 65 | return taskManager; 66 | } 67 | 68 | public activate(context: vscode.ExtensionContext) { 69 | this._context = context; 70 | this._info = this.checkExtension(context); 71 | if (!this._info.disable) { 72 | this.activateFeatures(); 73 | } 74 | } 75 | 76 | public dispose(): void { 77 | if (docLensProvider) { 78 | docLensProvider.dispose(); 79 | } 80 | if (taskManager) { 81 | taskManager.dispose(); 82 | } 83 | if (pwsh) { 84 | pwsh.dispose(); 85 | } 86 | if (logger) { 87 | logger.dispose(); 88 | } 89 | } 90 | 91 | private activateFeatures(): void { 92 | this.switchMode(); 93 | if (this._context) { 94 | this._context.subscriptions.push( 95 | vscode.workspace.onDidGrantWorkspaceTrust(() => { 96 | this.switchMode(); 97 | }) 98 | ); 99 | this._context.subscriptions.push( 100 | vscode.commands.registerCommand('PSRule.openOptionsFile', (path: string) => { 101 | openOptionsFile(path); 102 | }) 103 | ); 104 | this._context.subscriptions.push( 105 | vscode.commands.registerCommand('PSRule.createOptionsFile', (path: string) => { 106 | createOptionsFile(path); 107 | }) 108 | ); 109 | this._context.subscriptions.push( 110 | vscode.commands.registerCommand('PSRule.configureSettings', () => { 111 | configureSettings(); 112 | }) 113 | ); 114 | this._context.subscriptions.push( 115 | vscode.commands.registerCommand('PSRule.walkthroughCopySnippet', (args: { snippet: string }) => { 116 | walkthroughCopySnippet(args.snippet); 117 | }) 118 | ); 119 | this._context.subscriptions.push( 120 | vscode.commands.registerCommand('PSRule.runAnalysisTask', () => { 121 | runAnalysisTask(); 122 | }) 123 | ); 124 | this._context.subscriptions.push( 125 | vscode.commands.registerCommand('PSRule.showTasks', () => { 126 | showTasks(); 127 | }) 128 | ); 129 | } 130 | } 131 | 132 | private switchMode(): void { 133 | ConfigurationManager.configure(this._context); 134 | 135 | if (!docLensProvider) { 136 | docLensProvider = new DocumentationLensProvider(logger, this._context); 137 | docLensProvider.register(); 138 | } 139 | 140 | if (this.isTrusted) { 141 | this.setContextVariables(); 142 | } 143 | 144 | if (this.isTrusted) { 145 | pwsh.configure(this._info); 146 | } 147 | 148 | if (this.isTrusted) { 149 | taskManager = new PSRuleTaskProvider(logger, this._context); 150 | taskManager.register(); 151 | } 152 | } 153 | 154 | private setContextVariables(): void { 155 | vscode.commands.executeCommand('setContext', 'PSRule.workspaceTrusted', this.isTrusted); 156 | } 157 | 158 | /** 159 | * Check channel and version of the extension activated. 160 | * @param context An extension context. 161 | */ 162 | private checkExtension(context: vscode.ExtensionContext): ExtensionInfo { 163 | const extensionVersionKey = 'ps-rule-extension-version'; 164 | 165 | // Get channel 166 | let extensionId = 'bewhite.psrule-vscode'; 167 | let extensionChannel = 'stable'; 168 | const isStableInstalled = vscode.extensions.getExtension(extensionId) !== undefined; 169 | 170 | if (path.basename(context.globalStorageUri.fsPath) === 'bewhite.psrule-vscode-preview') { 171 | extensionId = 'bewhite.psrule-vscode-preview'; 172 | extensionChannel = 'preview'; 173 | } 174 | if (path.basename(context.globalStorageUri.fsPath) === 'bewhite.psrule-vscode-dev') { 175 | extensionId = 'bewhite.psrule-vscode-dev'; 176 | extensionChannel = 'dev'; 177 | } 178 | logger.verbose(`Running extension channel: ${extensionChannel}`); 179 | 180 | // Get current version 181 | const extension = vscode.extensions.getExtension(extensionId)!; 182 | const extensionVersion: string = extension.packageJSON.version; 183 | logger.verbose(`Running extension version: ${extensionVersion}`); 184 | 185 | // Get last version 186 | const lastVersion = context.globalState.get(extensionVersionKey); 187 | 188 | // Save the extension version 189 | context.globalState.update(extensionVersionKey, extensionVersion); 190 | 191 | // Determine if the channel upgrade message is shown 192 | const showChannelUpgrade: boolean = vscode.workspace 193 | .getConfiguration('PSRule.notifications') 194 | .get('showChannelUpgrade', true); 195 | 196 | const showExtension = 'Show Extension'; 197 | if ((extensionChannel === 'preview' || extensionChannel === 'dev') && showChannelUpgrade) { 198 | const showReleaseNotes = 'Show Release Notes'; 199 | const alwaysIgnore = 'Always Ignore'; 200 | 201 | vscode.window 202 | .showInformationMessage( 203 | `You are running the ${extensionChannel} version of PSRule. A stable version is available.`, 204 | showReleaseNotes, 205 | showExtension, 206 | alwaysIgnore 207 | ) 208 | .then((choice) => { 209 | if (choice === showReleaseNotes) { 210 | vscode.commands.executeCommand( 211 | 'markdown.showPreview', 212 | vscode.Uri.file(path.resolve(__dirname, '../../CHANGELOG.md')) 213 | ); 214 | } 215 | if (choice === showExtension) { 216 | vscode.commands.executeCommand( 217 | 'workbench.extensions.search', 218 | 'bewhite.psrule-vscode' 219 | ); 220 | } 221 | if (choice === alwaysIgnore) { 222 | vscode.workspace 223 | .getConfiguration('PSRule.notifications') 224 | .update('showChannelUpgrade', false, vscode.ConfigurationTarget.Global); 225 | } 226 | }); 227 | } 228 | 229 | let disable = false; 230 | if ((extensionChannel === 'preview' || extensionChannel === 'dev') && isStableInstalled) { 231 | disable = true; 232 | vscode.window 233 | .showWarningMessage( 234 | `You may experience issues running the ${extensionChannel} version of PSRule, side-by-side with the stable version. Please uninstall one of ${extensionChannel} or stable version and reload Visual Studio Code for the best experience.`, 235 | showExtension, 236 | ) 237 | .then((choice) => { 238 | if (choice === showExtension) { 239 | vscode.commands.executeCommand( 240 | 'workbench.extensions.search', 241 | 'PSRule' 242 | ); 243 | } 244 | }); 245 | } 246 | 247 | const result: ExtensionInfo = { 248 | id: extensionId, 249 | version: extensionVersion, 250 | channel: extensionChannel, 251 | path: context.extensionPath, 252 | disable: disable, 253 | }; 254 | return result; 255 | } 256 | } 257 | 258 | export const ext: ExtensionManager = new ExtensionManager(); 259 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | 'use strict'; 5 | 6 | import * as vscode from 'vscode'; 7 | 8 | export enum LogLevel { 9 | Normal, 10 | Warning, 11 | Error, 12 | Verbose, 13 | } 14 | 15 | // Define a logger 16 | export interface ILogger { 17 | verbose(message: string, ...additionalMessages: string[]): void; 18 | 19 | log(message: string, ...additionalMessages: string[]): void; 20 | 21 | dispose(): void; 22 | } 23 | 24 | export class Logger implements ILogger { 25 | private _LogLevel: LogLevel = LogLevel.Normal; 26 | private _Output: vscode.OutputChannel; 27 | 28 | constructor(channel: string) { 29 | this._Output = vscode.window.createOutputChannel(channel); 30 | } 31 | 32 | public dispose(): void { 33 | this._Output.dispose(); 34 | } 35 | 36 | public verbose(message: string, ...additionalMessages: string[]): void { 37 | this.write(LogLevel.Verbose, message, ...additionalMessages); 38 | } 39 | 40 | public log(message: string, ...additionalMessages: string[]): void { 41 | this.write(LogLevel.Normal, message, ...additionalMessages); 42 | } 43 | 44 | private write(logLevel: LogLevel, message: string, ...additionalMessages: string[]): void { 45 | if (logLevel >= this._LogLevel) { 46 | this.writeLine(message, logLevel); 47 | 48 | additionalMessages.forEach((line) => { 49 | this.writeLine(line, logLevel); 50 | }); 51 | } 52 | } 53 | 54 | private writeLine(message: string, level: LogLevel): void { 55 | this._Output.appendLine(message); 56 | } 57 | } 58 | 59 | export const logger: ILogger = new Logger('PSRule'); 60 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | 'use strict'; 5 | 6 | import * as vscode from 'vscode'; 7 | import { ext } from './extension'; 8 | 9 | /** 10 | * Activate PSRule extension. 11 | * @param context An extension context. 12 | */ 13 | export function activate(context: vscode.ExtensionContext): void { 14 | ext.activate(context); 15 | } 16 | 17 | /** 18 | * Deactivate PSRule extension. 19 | */ 20 | export function deactivate(): void { 21 | if (ext) { 22 | ext.dispose(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/powershell.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | 'use strict'; 5 | 6 | import * as vscode from 'vscode'; 7 | import { ext, ExtensionInfo } from './extension'; 8 | import { configuration, ConfigurationManager } from './configuration'; 9 | import { logger } from './logger'; 10 | 11 | /** 12 | * External interface from PowerShell extension. 13 | */ 14 | interface IExternalPowerShellDetails { 15 | exePath: string; 16 | version: string; 17 | displayName: string; 18 | architecture: string; 19 | } 20 | 21 | /** 22 | * External interface from PowerShell extension. 23 | */ 24 | interface IPowerShellExtensionClient { 25 | registerExternalExtension(id: string, apiVersion?: string): string; 26 | unregisterExternalExtension(uuid: string): boolean; 27 | getPowerShellVersionDetails(uuid: string): Promise; 28 | } 29 | 30 | export class PowerShellExtension implements vscode.Disposable { 31 | private readonly extension: vscode.Extension | undefined; 32 | 33 | private _version!: string; 34 | private _path!: string; 35 | private uuid!: string; 36 | 37 | constructor() { 38 | this.extension = this.getExtension(); 39 | } 40 | 41 | public get isActive(): boolean { 42 | return this.extension !== undefined && this.extension?.isActive; 43 | } 44 | 45 | public get path(): string { 46 | return this._path; 47 | } 48 | 49 | public configure(info: ExtensionInfo): void { 50 | // Determine if the install PowerShell extension notification is displayed 51 | const showPowerShellExtension: boolean = 52 | configuration.get().notificationsShowPowerShellExtension; 53 | 54 | if (this.extension !== undefined && !this.extension.isActive) { 55 | this.extension.activate().then((client) => { 56 | this.uuid = client.registerExternalExtension(info.id, 'v1'); 57 | client.getPowerShellVersionDetails(this.uuid).then((v) => this.handlePowerShell(v)); 58 | }); 59 | } else if (this.extension === undefined && showPowerShellExtension) { 60 | logger.verbose(`PowerShell extension is not installed.`); 61 | 62 | const showExtension = 'Show Extension'; 63 | const alwaysIgnore = 'Always Ignore'; 64 | 65 | vscode.window 66 | .showInformationMessage( 67 | `Some features require the PowerShell extension to be installed and enabled.`, 68 | showExtension, 69 | alwaysIgnore 70 | ) 71 | .then((choice) => { 72 | if (choice === showExtension) { 73 | vscode.commands.executeCommand( 74 | 'workbench.extensions.search', 75 | 'ms-vscode.PowerShell' 76 | ); 77 | } 78 | if (choice === alwaysIgnore) { 79 | vscode.workspace 80 | .getConfiguration('PSRule.notifications') 81 | .update( 82 | 'showPowerShellExtension', 83 | false, 84 | vscode.ConfigurationTarget.Global 85 | ); 86 | } 87 | }); 88 | } 89 | } 90 | 91 | public dispose(): void { 92 | if (this.extension && this.uuid !== undefined) { 93 | const powerShellExtensionClient = this.extension!.exports as IPowerShellExtensionClient; 94 | powerShellExtensionClient.unregisterExternalExtension(this.uuid); 95 | } 96 | } 97 | 98 | private handlePowerShell(value: IExternalPowerShellDetails): void { 99 | this._version = value.version; 100 | this._path = value.exePath; 101 | 102 | logger.verbose(`Using PowerShell ${this._version} from ${this._path}`); 103 | } 104 | 105 | private getExtension(): vscode.Extension | undefined { 106 | return ( 107 | vscode.extensions.getExtension( 108 | 'ms-vscode.powershell-preview' 109 | ) ?? vscode.extensions.getExtension('ms-vscode.powershell') 110 | ); 111 | } 112 | } 113 | 114 | export const pwsh = new PowerShellExtension(); 115 | -------------------------------------------------------------------------------- /src/tasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | 'use strict'; 5 | 6 | import * as fs from 'fs'; 7 | import * as path from 'path'; 8 | import * as vscode from 'vscode'; 9 | import { defaultOptionsFile } from './consts'; 10 | import { ILogger } from './logger'; 11 | import { configuration, ExecutionActionPreference } from './configuration'; 12 | import { pwsh } from './powershell'; 13 | 14 | const emptyTasks: vscode.Task[] = []; 15 | 16 | interface IContext { 17 | readonly logger: ILogger; 18 | readonly extensionContext: vscode.ExtensionContext; 19 | } 20 | 21 | interface PSRuleTaskDefinition extends vscode.TaskDefinition { 22 | /** 23 | * The a path to rules to use for analysis. 24 | */ 25 | path?: string; 26 | 27 | /** 28 | * The input path to run. 29 | */ 30 | inputPath?: string; 31 | 32 | /** 33 | * An optional baseline to use. 34 | */ 35 | baseline?: string; 36 | 37 | /** 38 | * Rule modules to use for analysis. 39 | */ 40 | modules?: string[]; 41 | 42 | outcome?: string[]; 43 | } 44 | 45 | /** 46 | * A task provider for PSRule. 47 | */ 48 | export class PSRuleTaskProvider implements vscode.TaskProvider { 49 | // Fields 50 | private readonly context: IContext; 51 | 52 | private static taskType: string = 'PSRule'; 53 | private tasks: vscode.Task[] | undefined; 54 | 55 | // We use a CustomExecution task when state needs to be shared across runs of the task or when 56 | // the task requires use of some VS Code API to run. 57 | // If you don't need to share state between runs and if you don't need to execute VS Code API in your task, 58 | // then a simple ShellExecution or ProcessExecution should be enough. 59 | // Since our build has this shared state, the CustomExecution is used below. 60 | private sharedState: string | undefined; 61 | private providerRegistration!: vscode.Disposable; 62 | 63 | constructor(logger: ILogger, extensionContext: vscode.ExtensionContext) { 64 | this.context = { logger, extensionContext }; 65 | } 66 | 67 | public dispose() { 68 | // Do nothing yet 69 | if (this.providerRegistration) { 70 | this.providerRegistration.dispose(); 71 | } 72 | } 73 | 74 | public register(): void { 75 | // Register a task provider 76 | this.providerRegistration = vscode.tasks.registerTaskProvider( 77 | PSRuleTaskProvider.taskType, 78 | this 79 | ); 80 | this.context.logger.verbose('Registered task provider.'); 81 | } 82 | 83 | /** 84 | * Get tasks for Visual Studio Code API. 85 | * @returns Returns a list of tasks. 86 | */ 87 | public async provideTasks(): Promise { 88 | return this.getTasks(); 89 | } 90 | 91 | /** 92 | * Complete a task object for Visual Studio Code. 93 | * @param _task A task object that might need completing. 94 | * @returns A completed Visual Studio Code task. 95 | */ 96 | public resolveTask(_task: vscode.Task): vscode.Task | undefined { 97 | const definition: PSRuleTaskDefinition = _task.definition; 98 | const scope = _task.scope as vscode.WorkspaceFolder; 99 | return this.resolveTaskRunAnalysis(scope, definition); 100 | } 101 | 102 | /** 103 | * Get a list of PSRule tasks for each Visual Studio Code workspace. 104 | * @returns A list of tasks. 105 | */ 106 | private async getTasks(): Promise { 107 | const folders = vscode.workspace.workspaceFolders; 108 | if (!folders) { 109 | return Promise.resolve([]); 110 | } 111 | 112 | const result: vscode.Task[] = []; 113 | for (let i = 0, len = folders.length; i < len; i++) { 114 | if (this.isEnabled(folders[i])) { 115 | const tasks = await this.getWorkspaceTasks(folders[i]); 116 | result.push(...tasks); 117 | } 118 | } 119 | return result; 120 | } 121 | 122 | private isEnabled(folder: vscode.WorkspaceFolder): boolean { 123 | return true; 124 | } 125 | 126 | private async exists(file: string): Promise { 127 | return new Promise((resolve) => { 128 | fs.exists(file, (value) => { 129 | resolve(value); 130 | }); 131 | }); 132 | } 133 | 134 | private async readFile(file: string): Promise { 135 | return new Promise((resolve, reject) => { 136 | fs.readFile(file, (err, data) => { 137 | if (err) { 138 | reject(err); 139 | } 140 | resolve(data.toString()); 141 | }); 142 | }); 143 | } 144 | 145 | public async getWorkspaceTasks(folder: vscode.WorkspaceFolder): Promise { 146 | if (folder.uri.scheme !== 'file') { 147 | return emptyTasks; 148 | } 149 | 150 | const rootPath = folder.uri.fsPath; 151 | const optionFilePath = path.join(rootPath, defaultOptionsFile); 152 | 153 | if (!(await this.exists(optionFilePath))) { 154 | return emptyTasks; 155 | } 156 | 157 | try { 158 | const result: vscode.Task[] = []; 159 | let t = this.createTaskRunAnalysis(folder); 160 | if (t !== undefined) result.push(t); 161 | return result; 162 | } catch (e) { 163 | return emptyTasks; 164 | } 165 | } 166 | 167 | private resolveTaskRunAnalysis( 168 | folder: vscode.WorkspaceFolder, 169 | definition: PSRuleTaskDefinition 170 | ): vscode.Task | undefined { 171 | return this.createTask( 172 | 'Run analysis', 173 | folder, 174 | definition.path, 175 | definition.inputPath, 176 | definition.baseline, 177 | definition.modules, 178 | definition.outcome, 179 | undefined, 180 | definition 181 | ); 182 | } 183 | 184 | private createTaskRunAnalysis(folder: vscode.WorkspaceFolder): vscode.Task | undefined { 185 | return this.createTask( 186 | 'Run analysis', 187 | folder, 188 | undefined, 189 | undefined, 190 | undefined, 191 | undefined, 192 | undefined 193 | ); 194 | } 195 | 196 | /** 197 | * Creates a task. 198 | * @param name The name of the task. 199 | * @param path The root of the workspace. 200 | * @param baseline An optional baseline. 201 | * @param matcher A task filter. 202 | */ 203 | private createTask( 204 | name: string, 205 | folder: vscode.WorkspaceFolder | undefined, 206 | path?: string, 207 | inputPath?: string, 208 | baseline?: string, 209 | modules?: string[], 210 | outcome?: string[], 211 | matcher?: any, 212 | definition?: PSRuleTaskDefinition 213 | ): vscode.Task { 214 | if (definition === undefined) { 215 | definition = { 216 | type: PSRuleTaskProvider.taskType, 217 | matcher: '$PSRule', 218 | }; 219 | } 220 | 221 | const executionNotProcessedWarning = configuration.get().executionNotProcessedWarning; 222 | const executionRuleExcluded = configuration.get().executionRuleExcluded; 223 | const executionRuleSuppressed = configuration.get().executionRuleSuppressed; 224 | const executionUnprocessedObject = configuration.get().executionUnprocessedObject; 225 | const outputAs = configuration.get().outputAs; 226 | const ruleBaseline = configuration.get().ruleBaseline; 227 | 228 | function getTaskName() { 229 | return name; 230 | } 231 | 232 | function getCmd(): string { 233 | let params = ''; 234 | 235 | // Path 236 | if (path !== undefined && path !== '') { 237 | params += ` -Path '${path}'`; 238 | } else { 239 | params += " -Path './.ps-rule/'"; 240 | } 241 | 242 | if (inputPath !== undefined && inputPath !== '') { 243 | params += ` -InputPath '${inputPath}'`; 244 | } else { 245 | params += ` -InputPath .`; 246 | } 247 | 248 | // Baseline 249 | if (baseline !== undefined && baseline !== '') { 250 | params += ` -Baseline '${baseline}'`; 251 | } else if (ruleBaseline !== undefined && ruleBaseline !== '') { 252 | params += ` -Baseline '${ruleBaseline}'`; 253 | } 254 | 255 | // Modules 256 | if (modules !== undefined && modules.length > 0) { 257 | for (let i = 0; i < modules.length; i++) { 258 | if (i > 0) { 259 | params += `, ${modules[i]}`; 260 | } else { 261 | params += ` -Module ${modules[i]}`; 262 | } 263 | } 264 | } 265 | 266 | // Outcome 267 | if (outcome !== undefined && outcome.length > 0) { 268 | for (let i = 0; i < outcome.length; i++) { 269 | if (i > 0) { 270 | params += `, ${outcome[i]}`; 271 | } else { 272 | params += ` -Outcome ${outcome[i]}`; 273 | } 274 | } 275 | } else { 276 | params += ' -Outcome Fail, Error'; 277 | } 278 | 279 | return `Assert-PSRule -Format File${params};`; 280 | } 281 | 282 | const taskName = getTaskName(); 283 | 284 | if (!pwsh.isActive) { 285 | return new vscode.Task( 286 | definition, 287 | folder ?? vscode.TaskScope.Workspace, 288 | taskName, 289 | PSRuleTaskProvider.taskType, 290 | new vscode.CustomExecution(async (): Promise => { 291 | // When the task is executed, this callback will run. Here, we setup for running the task. 292 | return new NoPowerShellPseudoterminal(); 293 | }), 294 | matcher 295 | ); 296 | } 297 | 298 | // Set environment variables for the task. 299 | let taskEnv: { [key: string]: string } = { 300 | PSRULE_OUTPUT_STYLE: 'VisualStudioCode', 301 | PSRULE_OUTPUT_AS: outputAs, 302 | PSRULE_OUTPUT_CULTURE: vscode.env.language, 303 | PSRULE_OUTPUT_BANNER: 'Minimal', 304 | }; 305 | 306 | if (executionNotProcessedWarning !== undefined) { 307 | taskEnv.PSRULE_EXECUTION_NOTPROCESSEDWARNING = executionNotProcessedWarning 308 | ? 'true' 309 | : 'false'; 310 | } 311 | 312 | if (executionRuleExcluded !== undefined && executionRuleExcluded !== ExecutionActionPreference.None) { 313 | taskEnv.PSRULE_EXECUTION_RULEEXCLUDED = executionRuleExcluded; 314 | } 315 | 316 | if (executionRuleSuppressed !== undefined && executionRuleSuppressed !== ExecutionActionPreference.None) { 317 | taskEnv.PSRULE_EXECUTION_RULESUPPRESSED = executionRuleSuppressed; 318 | } 319 | 320 | if (executionUnprocessedObject !== undefined && executionUnprocessedObject !== ExecutionActionPreference.None) { 321 | taskEnv.PSRULE_EXECUTION_UNPROCESSEDOBJECT = executionUnprocessedObject; 322 | } 323 | 324 | // Return the task instance. 325 | const t = new vscode.Task( 326 | definition, 327 | folder ?? vscode.TaskScope.Workspace, 328 | taskName, 329 | PSRuleTaskProvider.taskType, 330 | new vscode.ShellExecution(getCmd(), { 331 | executable: pwsh.path, 332 | shellArgs: ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command'], 333 | env: taskEnv, 334 | }), 335 | matcher 336 | ); 337 | t.detail = 'Run analysis for current workspace.'; 338 | return t; 339 | } 340 | } 341 | 342 | class NoPowerShellPseudoterminal implements vscode.Pseudoterminal { 343 | private writeEmitter = new vscode.EventEmitter(); 344 | private closeEmitter = new vscode.EventEmitter(); 345 | 346 | onDidWrite: vscode.Event = this.writeEmitter.event; 347 | onDidClose?: vscode.Event = this.closeEmitter.event; 348 | 349 | open(initialDimensions: vscode.TerminalDimensions | undefined): void { 350 | this.run(); 351 | } 352 | close(): void { } 353 | 354 | private async run(): Promise { 355 | return new Promise((resolve) => { 356 | this.writeEmitter.fire( 357 | 'The extension PowerShell must be installed and enabled to run this task.' 358 | ); 359 | this.closeEmitter.fire(); 360 | resolve(); 361 | }); 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as path from 'path'; 5 | import { runTests } from '@vscode/test-electron'; 6 | 7 | async function main() { 8 | try { 9 | // The folder containing the Extension Manifest package.json 10 | // Passed to `--extensionDevelopmentPath` 11 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 12 | 13 | // The path to the extension test script 14 | // Passed to --extensionTestsPath 15 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 16 | 17 | // Download VS Code, unzip it and run the integration test 18 | await runTests({ 19 | extensionDevelopmentPath, 20 | extensionTestsPath, 21 | launchArgs: ['--disable-extension=bewhite.psrule-vscode-preview', '--disable-extension=bewhite.psrule-vscode'] 22 | }); 23 | } catch (err) { 24 | console.error('Failed to run tests'); 25 | process.exit(1); 26 | } 27 | } 28 | 29 | main(); 30 | -------------------------------------------------------------------------------- /src/test/suite/configuration.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as assert from 'assert'; 5 | import { ConfigurationManager, ExecutionActionPreference, OutputAs } from '../../configuration'; 6 | 7 | suite('ConfigurationManager tests', () => { 8 | test('Defaults', () => { 9 | const config = new ConfigurationManager(undefined, 'PSRule_unit_test'); 10 | 11 | assert.equal(config.get().codeLensRuleDocumentationLinks, true); 12 | assert.equal(config.get().documentationCustomSnippetPath, undefined); 13 | assert.equal(config.get().documentationLocalePath, 'en'); 14 | assert.equal(config.get().documentationPath, undefined); 15 | assert.equal(config.get().documentationSnippet, 'Rule Doc'); 16 | assert.equal(config.get().executionNotProcessedWarning, undefined); 17 | assert.equal(config.get().executionRuleExcluded, ExecutionActionPreference.None); 18 | assert.equal(config.get().executionRuleSuppressed, ExecutionActionPreference.None); 19 | assert.equal(config.get().executionUnprocessedObject, ExecutionActionPreference.None); 20 | assert.equal(config.get().experimentalEnabled, false); 21 | assert.equal(config.get().outputAs, OutputAs.Summary); 22 | assert.equal(config.get().notificationsShowChannelUpgrade, true); 23 | assert.equal(config.get().notificationsShowPowerShellExtension, true); 24 | assert.equal(config.get().ruleBaseline, undefined); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as assert from 'assert'; 5 | import * as Extension from '../../extension'; 6 | 7 | suite('Extension tests', () => { 8 | test('Get extension info', () => { 9 | Extension.ext.info 10 | .then((info) => { 11 | assert.strictEqual('dev', info.channel); 12 | assert.strictEqual('0.0.1', info.version); 13 | assert.strictEqual('bewhite.psrule-vscode-dev', info.id); 14 | }) 15 | .catch((reason) => { 16 | assert.fail(`Failed to get extension info. ${reason}`); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as path from 'path'; 5 | import * as Mocha from 'mocha'; 6 | import * as glob from 'glob'; 7 | 8 | export function run(): Promise { 9 | // Create the mocha test 10 | const mocha = new Mocha({ 11 | ui: 'tdd', 12 | color: true, 13 | }); 14 | 15 | const testsRoot = path.resolve(__dirname, '..'); 16 | 17 | return new Promise((c, e) => { 18 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 19 | if (err) { 20 | return e(err); 21 | } 22 | 23 | // Add files to the test suite 24 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 25 | 26 | try { 27 | // Run the mocha test 28 | mocha.run((failures) => { 29 | if (failures > 0) { 30 | e(new Error(`${failures} tests failed.`)); 31 | } else { 32 | c(); 33 | } 34 | }); 35 | } catch (err) { 36 | console.error(err); 37 | e(err); 38 | } 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /src/test/suite/tasks.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as assert from 'assert'; 5 | import * as Extension from '../../extension'; 6 | 7 | suite('PSRuleTaskProvider tests', () => { 8 | test('Call taskManager', () => { 9 | Extension.taskManager 10 | ?.provideTasks() 11 | .then((t) => { 12 | assert.strictEqual(1, t.length); 13 | }) 14 | .catch((reason) => { 15 | assert.fail(`Failed to get tasks. ${reason}`); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | import * as fse from 'fs-extra'; 5 | import * as os from 'os'; 6 | import * as path from 'path'; 7 | import { SnippetString, Uri, WorkspaceFolder, window, workspace } from 'vscode'; 8 | import { configuration } from './configuration'; 9 | import { ext } from './extension'; 10 | 11 | /** 12 | * Calculates the file path of rule documentation for a specific rule based on settings. 13 | * @param name The name of the rule. 14 | * @returns The path where the rule markdown documentation should be created/ edited from. 15 | */ 16 | export async function getDocumentationPath(name: string): Promise { 17 | const workspaceRoot = getActiveOrFirstWorkspace()?.uri; 18 | const docConfigPath = configuration.get().documentationPath; 19 | let docRootPath = 20 | docConfigPath && workspaceRoot ? path.join(workspaceRoot.fsPath, docConfigPath) : undefined; 21 | let lang = configuration.get().documentationLocalePath; 22 | 23 | if (!docRootPath && window.activeTextEditor?.document.uri) { 24 | docRootPath = path.dirname(window.activeTextEditor.document.uri.path); 25 | } 26 | if (docRootPath) { 27 | let uri = Uri.file(path.join(docRootPath, lang, `${name}.md`)); 28 | return uri; 29 | } 30 | return undefined; 31 | } 32 | 33 | /** 34 | * Get a snippet from disk. 35 | * @param file The path to a file containing a snippet. When not set, the default extension snippets will be used. 36 | * @param name The name of the snippet to use. When name is an empty string no snippet is returned. 37 | * @returns A possible matching snippet string. 38 | */ 39 | export async function readDocumentationSnippet( 40 | file: string | undefined, 41 | name: string 42 | ): Promise { 43 | if (name === '') return undefined; 44 | 45 | // Try custom snippet file 46 | const workspaceRoot = getActiveOrFirstWorkspace()?.uri; 47 | let snippetFile = file && workspaceRoot ? path.join(workspaceRoot.fsPath, file) : undefined; 48 | 49 | // Try built-in snippet file 50 | if (!snippetFile) { 51 | const info = await ext.info; 52 | snippetFile = info ? path.join(info.path, 'snippets/markdown.json') : undefined; 53 | } 54 | 55 | if (snippetFile && (await fse.pathExists(snippetFile))) { 56 | let json = await fse.readJson(snippetFile, { encoding: 'utf-8' }); 57 | if (json) { 58 | let body: string[] = json[name].body; 59 | return new SnippetString(body.join(os.EOL)); 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Get options snippet. 66 | * @param name The name of the snippet to use. When name is an empty string no snippet is returned. 67 | * @returns A possible matching snippet string. 68 | */ 69 | export async function readOptionsSnippet(name: string): Promise { 70 | if (name === '') return undefined; 71 | 72 | // Try built-in snippet file 73 | const info = await ext.info; 74 | const snippetFile = info ? path.join(info.path, 'snippets/options.json') : undefined; 75 | 76 | if (snippetFile && (await fse.pathExists(snippetFile))) { 77 | let json = await fse.readJson(snippetFile, { encoding: 'utf-8' }); 78 | if (json) { 79 | let body: string[] = json[name].body; 80 | return new SnippetString(body.join(os.EOL)); 81 | } 82 | } 83 | } 84 | 85 | export function getActiveOrFirstWorkspace(): WorkspaceFolder | undefined { 86 | if (window.activeTextEditor) { 87 | return workspace.getWorkspaceFolder(window.activeTextEditor.document.uri); 88 | } 89 | return workspace.workspaceFolders && workspace.workspaceFolders.length > 0 90 | ? workspace.workspaceFolders[0] 91 | : undefined; 92 | } 93 | -------------------------------------------------------------------------------- /syntaxes/comments.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "PSRule-powershell-comments", 3 | "injectionSelector": "L:source.powershell", 4 | "patterns": [ 5 | { 6 | "include": "#synopsis-comment" 7 | } 8 | ], 9 | "repository": { 10 | "synopsis-comment": { 11 | "match": "(?<=^# )(Synopsis:|Description:)(?= )", 12 | "name": "keyword.operator.documentation.powershell" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /syntaxes/keywords.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "scopeName": "PSRule-powershell-keywords", 4 | "injectionSelector": "L:meta.scriptblock.powershell", 5 | "patterns": [ 6 | { 7 | "include": "#built-in-keyword" 8 | } 9 | ], 10 | "repository": { 11 | "built-in-keyword": { 12 | "match": "(?>^\\s*|\\|\\s*)\\b(?i:AllOf|AnyOf|Exists|Match|Within|TypeOf|Recommend|Reason)\\b", 13 | "name": "keyword.control.powershell" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /syntaxes/rule.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "PSRule-powershell-rule", 3 | "injectionSelector": "L:source.powershell", 4 | "patterns": [ 5 | { 6 | "include": "#rule-keyword" 7 | } 8 | ], 9 | "repository": { 10 | "rule-keyword": { 11 | "match": "(?<=^)(?i)(rule)(?=\\s+)", 12 | "name": "storage.type.powershell" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /syntaxes/yaml-comments.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeName": "PSRule-yaml-comments", 3 | "injectionSelector": "L:comment.line.number-sign.yaml", 4 | "patterns": [ 5 | { 6 | "include": "#synopsis-comment" 7 | } 8 | ], 9 | "repository": { 10 | "synopsis-comment": { 11 | "match": "Synopsis:", 12 | "name": "keyword.operator.documentation.powershell" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "rootDir": "src", 6 | "outDir": "out/dist", 7 | "lib": ["es6"], 8 | "sourceMap": true, 9 | "strict": true 10 | }, 11 | "exclude": ["node_modules", ".vscode-test"] 12 | } 13 | --------------------------------------------------------------------------------