├── .github
└── workflows
│ └── workflow.createprcomment.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CreatePrComment
├── .gitignore
├── CONTRIBUTION.md
├── LICENSE
├── LICENSE.md
├── README.md
├── docs
│ └── images
│ │ ├── Comment.png
│ │ ├── CommentTask.png
│ │ ├── CreatePRCommentTask.png
│ │ └── ServiceConnection.png
├── package-lock.json
├── package.json
├── src
│ ├── task.ts
│ └── variableresolver.ts
├── task
│ ├── CreatePRCommentTaskV0
│ │ ├── icon.png
│ │ └── task.json
│ └── CreatePRCommentTaskV1
│ │ ├── icon.png
│ │ └── task.json
├── tests
│ └── L0.ts
├── tsconfig.json
├── vss-extension-icon.png
└── vss-extension.json
├── LICENSE
├── PipelineRunnerExtension
├── README.MD
└── images
│ ├── connection-icon.png
│ ├── icon.png
│ ├── screenshot-1.png
│ ├── screenshot-2.png
│ ├── screenshot-3.png
│ └── screenshot-4.png
├── README.md
├── SECURITY.md
└── Zap
├── README.md
└── docs
└── images
├── scan-results-collapsed.png
├── scan-results-drill-down.png
├── zap-aggressive.png
├── zap-context-empty.png
├── zap-context-provided.png
├── zap-scanner-config.png
├── zap-scanner-task.png
└── zap-target.png
/.github/workflows/workflow.createprcomment.yml:
--------------------------------------------------------------------------------
1 | name: 'CreatePrComment'
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 | paths:
9 | - "CreatePrComment/**"
10 | workflow_dispatch:
11 |
12 | env:
13 | WORKDIR: "CreatePrComment"
14 |
15 | defaults:
16 | run:
17 | working-directory: "CreatePrComment"
18 |
19 | jobs:
20 | build:
21 | strategy:
22 | matrix:
23 | os:
24 | - ubuntu-latest
25 | - macos-latest
26 | - windows-latest
27 | node-version: [10, 14]
28 | runs-on: ${{ matrix.os }}
29 | steps:
30 | - uses: actions/checkout@v2
31 | - name: "Use Node.js ${{ matrix.node-version }} on ${{ matrix.os }}"
32 | uses: actions/setup-node@v2
33 | with:
34 | node-version: ${{ matrix.node-version }}
35 | cache: npm
36 | cache-dependency-path: "${{ env.WORKDIR }}/package-lock.json"
37 | - run: npm ci
38 | - run: npm run build --if-present
39 | - run: npm run test --if-present
40 | - run: npm run report --if-present
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 |
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 |
28 | # Bower dependency directory (https://bower.io/)
29 | bower_components
30 |
31 | # node-waf configuration
32 | .lock-wscript
33 |
34 | # Compiled binary addons (https://nodejs.org/api/addons.html)
35 | build/Release
36 |
37 | # Dependency directories
38 | node_modules/
39 | jspm_packages/
40 |
41 | # TypeScript v1 declaration files
42 | typings/
43 |
44 | # Optional npm cache directory
45 | .npm
46 |
47 | # Optional eslint cache
48 | .eslintcache
49 |
50 | # Optional REPL history
51 | .node_repl_history
52 |
53 | # Output of 'npm pack'
54 | *.tgz
55 |
56 | # Yarn Integrity file
57 | .yarn-integrity
58 |
59 | # dotenv environment variables file
60 | .env
61 |
62 | # next.js build output
63 | .next
64 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/CreatePrComment/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.js
3 | *.vsix
4 | *.map
5 | .vscode
6 | test-results.xml
7 | .nyc_output
8 | .coverage_output
9 | .bin
10 | .dist
11 | .taskkey
--------------------------------------------------------------------------------
/CreatePrComment/CONTRIBUTION.md:
--------------------------------------------------------------------------------
1 | # Contribution
2 |
3 | This article explains how to build an development environment.
4 |
5 | ## Prerequisite
6 |
7 | - Node.js 14.0.x or later and NPM
8 | - [TFS Cross Platform Command Line Interface(tfx-cli)](https://github.com/microsoft/tfs-cli) 0.9.3 or latter
9 | - [Typescript](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) 4.4.4 or latter
10 |
11 | ## installation
12 |
13 | Clone this repo. Then install the npm packages.
14 |
15 | ```shell
16 | npm install
17 | ```
18 |
19 | ## npm commands
20 |
21 | ### Build
22 |
23 | Compile the TypeScript files to the js file.
24 |
25 | ```shell
26 | npm run build
27 | ```
28 |
29 | ### Pack
30 |
31 | Compile and Pack the code to the task directory
32 |
33 | ```shell
34 | npm run pack
35 | ```
36 |
37 | ### Create
38 |
39 | Compile, Pack and Create an extension `vsix` file.
40 |
41 | ```shell
42 | npm run create
43 | ```
44 |
45 | ### Test
46 |
47 | Currently Not Supported, however, it is coming soon.
48 |
--------------------------------------------------------------------------------
/CreatePrComment/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.
--------------------------------------------------------------------------------
/CreatePrComment/LICENSE.md:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/CreatePrComment/README.md:
--------------------------------------------------------------------------------
1 | # Create PR Comment Task
2 |
3 | 
4 |
5 | Create a Pull Request comment if a CI is triggered by Pull Request.
6 |
7 | ## How to use
8 |
9 | ### Configuration
10 |
11 | Install this extension to your project. Find the CreatePRCommentTask.
12 |
13 | 
14 |
15 | ### Details
16 |
17 | 
18 |
19 | | Name | Description |
20 | | ---------------- | ------------------------------------------------------------------------------------------------ |
21 | | Azure DevOps PAT | Select Azure DevOps Personal Access Token. or you can create new one |
22 | | Comment | If the pipeline is executed by Pull Request Validation, this task create a Pull Request Comment. |
23 |
24 | On the Comment, you can use Variables. The variables will be substituted by the actual value. e.g. `$(CWI.Id)`.
25 | The comment becomes message body of your Pull Request Comment.
26 |
27 | ### Personal Access Token Service Connection
28 |
29 | Put your Azure DevOps Personal Access Token in `PAT`. The PAT requires permission to write Code. For more detail, [Pull Request Thread Comments - Create](https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-thread-comments/create?view=azure-devops-rest-6.1). `Connection name` is just a label of this service connection. `Server URL` is not used currently, however it might be good as memo which you use it for.
30 |
31 | 
32 |
33 | ### Example
34 |
35 | Sample of the Comment.
36 |
37 | ```text
38 | CredScan reports a Bug. Please review it.
39 | ```
40 |
41 | ## Contribution
42 |
43 | For more details [here](https://github.com/microsoft/CSEDevOps/blob/main/CreatePrComment/CONTRIBUTION.md).
44 |
--------------------------------------------------------------------------------
/CreatePrComment/docs/images/Comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/CreatePrComment/docs/images/Comment.png
--------------------------------------------------------------------------------
/CreatePrComment/docs/images/CommentTask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/CreatePrComment/docs/images/CommentTask.png
--------------------------------------------------------------------------------
/CreatePrComment/docs/images/CreatePRCommentTask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/CreatePrComment/docs/images/CreatePRCommentTask.png
--------------------------------------------------------------------------------
/CreatePrComment/docs/images/ServiceConnection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/CreatePrComment/docs/images/ServiceConnection.png
--------------------------------------------------------------------------------
/CreatePrComment/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-pr-comment-task",
3 | "version": "1.0.0",
4 | "description": "Create a comment on a Pull Request",
5 | "main": "task.js",
6 | "scripts": {
7 | "clean": "rimraf ./.bin && rimraf ./.dist && rimraf ./*.vsix",
8 | "deps": "rimraf ./node_modules && npm install",
9 | "build": "rimraf ./.bin && tsc -p .",
10 | "start": "copyfiles -f ./task/CreatePRCommentTaskV1/task.json ./src && ts-node ./src/task.ts && rimraf ./src/task.json",
11 | "pack": "rimraf ./.dist && copyfiles vss-extension.json vss-extension-icon.png LICENSE.md README.md \"./task/**\" ./.dist && copyfiles -f package.json \"./.bin/*.js\" \"./.bin/**/*.js\" -e \"./.bin/tests/*\" ./.dist/task/CreatePRCommentTaskV0 && copyfiles -f package.json \"./.bin/*.js\" \"./.bin/**/*.js\" -e \"./.bin/tests/*\" ./.dist/task/CreatePRCommentTaskV1 && loop \"npm install --only=prod\" --cwd ./.dist/task",
12 | "create": "tfx extension create -r ./.dist",
13 | "createdev": "tfx extension create --rev-version --root ./.dist --publisher daporo-dev",
14 | "test": "tsc -p . && copyfiles -f ./task/CreatePRCommentTaskV1/task.json ./.bin/src && nyc mocha ./.bin/tests/L0.js",
15 | "report": "tsc -p . && mocha ./.bin/tests/L0.js --reporter mocha-junit-reporter && nyc report",
16 | "lint": "ts-standard",
17 | "lintfix": "ts-standard --fix"
18 | },
19 | "nyc": {
20 | "extension": [
21 | ".ts"
22 | ],
23 | "include": [
24 | "src"
25 | ],
26 | "reporter": [
27 | "text",
28 | "cobertura",
29 | "html"
30 | ],
31 | "report-dir": "./.coverage_output/coverage",
32 | "all": "true",
33 | "check-coverage": true,
34 | "statements": 70,
35 | "functions": 70,
36 | "branches": 70,
37 | "lines": 70
38 | },
39 | "repository": {
40 | "type": "git",
41 | "url": "git+https://github.com/microsoft/CSEDevOps.git"
42 | },
43 | "keywords": [
44 | "Pull Request",
45 | "PR",
46 | "PR Comment",
47 | "Utility task",
48 | "Utility",
49 | "Azure Pipelines"
50 | ],
51 | "author": "CSE-DevOps",
52 | "license": "MIT",
53 | "bugs": {
54 | "url": "https://github.com/microsoft/CSEDevOps/issues?q=is:issue+label:CreatePrComment"
55 | },
56 | "homepage": "https://github.com/microsoft/CSEDevOps/tree/main/CreatePrComment",
57 | "dependencies": {
58 | "azure-devops-node-api": "^11.1.0",
59 | "azure-pipelines-task-lib": "^3.1.10"
60 | },
61 | "devDependencies": {
62 | "@types/chai": "^4.3.0",
63 | "@types/mocha": "^9.1.0",
64 | "@types/node": "^17.0.18",
65 | "@types/q": "^1.5.5",
66 | "@types/sinon": "^10.0.11",
67 | "chai": "^4.3.6",
68 | "copyfiles": "^2.4.1",
69 | "loop": "^3.3.6",
70 | "mocha": "^9.2.0",
71 | "mocha-junit-reporter": "^2.0.2",
72 | "nyc": "^15.1.0",
73 | "rewiremock": "^3.14.3",
74 | "rimraf": "^3.0.2",
75 | "sinon": "^13.0.1",
76 | "source-map-support": "^0.5.21",
77 | "tfx-cli": "^0.10.0",
78 | "ts-node": "^10.5.0",
79 | "typescript": "^4.5.5"
80 | }
81 | }
--------------------------------------------------------------------------------
/CreatePrComment/src/task.ts:
--------------------------------------------------------------------------------
1 | import * as tl from 'azure-pipelines-task-lib'
2 | import * as wa from 'azure-devops-node-api/WebApi'
3 | import * as GitInterfaces from 'azure-devops-node-api/interfaces/GitInterfaces'
4 | import VariableResolver from './variableresolver'
5 | import { IGitApi, GitApi } from 'azure-devops-node-api/GitApi'
6 | import path from 'path'
7 | import { IRequestHandler } from 'azure-devops-node-api/interfaces/common/VsoBaseInterfaces'
8 |
9 | export interface IClientFactory {
10 | create: () => Promise
11 | }
12 |
13 | class ClientFactory implements IClientFactory {
14 | public async create (): Promise {
15 | const authType = tl.getInput('AuthType') || 'patService' // default for V0
16 | let credHandler: IRequestHandler
17 |
18 | switch (authType) {
19 | case 'patService': {
20 | const patService = tl.getInput('AzureDevOpsService')!
21 | const pat = tl.getEndpointAuthorizationParameter(patService, 'pat', false)!
22 | credHandler = wa.getPersonalAccessTokenHandler(pat)
23 | break
24 | }
25 | case 'pat': {
26 | const pat = tl.getInput('AzureDevOpsPat')!
27 | credHandler = wa.getPersonalAccessTokenHandler(pat)
28 | break
29 | }
30 | case 'system': {
31 | const token = tl.getVariable('System.AccessToken')!
32 | credHandler = wa.getBearerHandler(token)
33 | break
34 | }
35 | default:
36 | throw 'Unknown authentication type'
37 | }
38 |
39 | const connection = new wa.WebApi(tl.getVariable('System.TeamFoundationCollectionUri')!, credHandler)
40 | return await connection.getGitApi()
41 | }
42 | }
43 |
44 | export class CreatePRCommentTask {
45 | factory: IClientFactory
46 |
47 | constructor (clientFactory: IClientFactory) {
48 | this.factory = clientFactory
49 | }
50 |
51 | public async run (): Promise {
52 | try {
53 | tl.setResourcePath(path.join(__dirname, 'task.json'), true)
54 | const commentOriginal = tl.getInput('Comment', true)!
55 | tl.debug('commentOriginal:' + commentOriginal)
56 | const comment = VariableResolver.resolveVariables(commentOriginal)
57 | tl.debug('comment:' + comment)
58 |
59 | const client = await this.factory.create()
60 |
61 | const commentObject = {
62 | content: comment,
63 | commentType: GitInterfaces.CommentType.System
64 | }
65 | const thread: GitInterfaces.GitPullRequestCommentThread = {
66 | comments: [
67 | commentObject
68 | ],
69 | status: GitInterfaces.CommentThreadStatus.ByDesign
70 | }
71 | const repositoryId = tl.getVariable('Build.Repository.ID')!
72 | const pullRequestIdString = tl.getVariable('System.PullRequest.PullRequestId')
73 |
74 | if (pullRequestIdString === undefined) {
75 | // If the build is not pull request, do nothing.
76 | return
77 | }
78 |
79 | const pullRequestId: number = pullRequestIdString ? parseInt(pullRequestIdString) : 0
80 |
81 | const currentThreads = await client.getThreads(repositoryId, pullRequestId)
82 | for (var currentThread of currentThreads) {
83 | if (currentThread.comments !== null && currentThread.comments !== undefined) {
84 | for (var threadComment of currentThread.comments) {
85 | if (threadComment.content === comment) {
86 | return // If the same comment is already there.
87 | }
88 | }
89 | }
90 | }
91 |
92 | if (pullRequestId != 0) {
93 | const createdThread = await client.createThread(thread, repositoryId, pullRequestId)
94 | }
95 | } catch (e) {
96 | throw new Error(tl.loc('FailToCreateComment', e))
97 | }
98 | }
99 | }
100 |
101 | new CreatePRCommentTask(new ClientFactory()).run()
102 |
--------------------------------------------------------------------------------
/CreatePrComment/src/variableresolver.ts:
--------------------------------------------------------------------------------
1 | import { getVariable, debug } from 'azure-pipelines-task-lib'
2 |
3 | export default class VariableResolver {
4 | private static readonly variableRegExp = /\$\(([^)]+)\)/g
5 |
6 | public static resolveVariables (origValue: string): string {
7 | let newValue = origValue
8 |
9 | let match: RegExpExecArray | null
10 | while ((match = this.variableRegExp.exec(newValue)) !== null) {
11 | const variableValue = getVariable(match[1])
12 | if (variableValue && variableValue !== '') {
13 | newValue = this.replaceAll(newValue, match[0], variableValue)
14 | this.variableRegExp.lastIndex = 0
15 | } else {
16 | debug('Variable \'' + match[1] + '\' not defined.')
17 | }
18 | }
19 |
20 | return newValue
21 | }
22 |
23 | private static escapeRegExp (expression: string): string {
24 | return expression.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1')
25 | }
26 |
27 | private static replaceAll (origValue: string, searchValue: string, replaceValue: string) {
28 | return origValue.replace(new RegExp(this.escapeRegExp(searchValue), 'g'), replaceValue)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/CreatePrComment/task/CreatePRCommentTaskV0/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/CreatePrComment/task/CreatePRCommentTaskV0/icon.png
--------------------------------------------------------------------------------
/CreatePrComment/task/CreatePRCommentTaskV0/task.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "0fef745c-60e7-4f36-8541-e4d9ce6729b1",
3 | "name": "CreatePRCommentTask",
4 | "friendlyName": "Create PR comment task",
5 | "description": "Create a Pull Request comment from pipeline",
6 | "helpMarkDown": "[CreatePrComment](https://github.com/microsoft/CSEDevOps/tree/main/CreatePrComment)",
7 | "category": "Utility",
8 | "author": "CSE-DevOps",
9 | "preview": true,
10 | "version": {
11 | "Major": 0,
12 | "Minor": 1,
13 | "Patch": 15
14 | },
15 | "instanceNameFormat": "Create PR Comment",
16 | "inputs": [
17 | {
18 | "name": "AzureDevOpsService",
19 | "type": "connectedService:azuredevops",
20 | "label": "Azure DevOps PAT",
21 | "required": true,
22 | "helpMarkDown": "Select the Azure DevOps PAT"
23 | },
24 | {
25 | "name": "Comment",
26 | "type": "multiLine",
27 | "label": "Comment",
28 | "required": true,
29 | "helpMarkDown": "Comment which is created as an Pull Request comment"
30 | }
31 | ],
32 | "execution": {
33 | "Node10": {
34 | "target": "task.js"
35 | }
36 | },
37 | "messages": {
38 | "FailToCreateComment": "Failed to create a comment. For more details: %s"
39 | }
40 | }
--------------------------------------------------------------------------------
/CreatePrComment/task/CreatePRCommentTaskV1/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/CreatePrComment/task/CreatePRCommentTaskV1/icon.png
--------------------------------------------------------------------------------
/CreatePrComment/task/CreatePRCommentTaskV1/task.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "0fef745c-60e7-4f36-8541-e4d9ce6729b1",
3 | "name": "CreatePRCommentTask",
4 | "friendlyName": "Create PR comment task",
5 | "description": "Create a Pull Request comment from pipeline",
6 | "helpMarkDown": "[CreatePrComment](https://github.com/microsoft/CSEDevOps/tree/main/CreatePrComment)",
7 | "category": "Utility",
8 | "author": "CSE-DevOps",
9 | "preview": true,
10 | "version": {
11 | "Major": 1,
12 | "Minor": 0,
13 | "Patch": 0
14 | },
15 | "instanceNameFormat": "Create PR Comment",
16 | "inputs": [
17 | {
18 | "name": "AuthType",
19 | "label": "Authentication type",
20 | "type": "pickList",
21 | "defaultValue": "system",
22 | "required": true,
23 | "helpMarkDown": "Use personal or system authorization token",
24 | "properties": {
25 | "editableOptions": false
26 | },
27 | "options": {
28 | "system": "System Access Token",
29 | "pat": "Personal Access Token"
30 | }
31 | },
32 | {
33 | "name": "AzureDevOpsPat",
34 | "type": "string",
35 | "label": "Azure DevOps PAT",
36 | "required": true,
37 | "helpMarkDown": "Provide the Azure DevOps PAT",
38 | "visibleRule": "AuthType = pat"
39 | },
40 | {
41 | "name": "Comment",
42 | "type": "multiLine",
43 | "label": "Comment",
44 | "required": true,
45 | "helpMarkDown": "Comment which is created as an Pull Request comment"
46 | }
47 | ],
48 | "execution": {
49 | "Node10": {
50 | "target": "task.js"
51 | }
52 | },
53 | "messages": {
54 | "FailToCreateComment": "Failed to create a comment. For more details: %s"
55 | }
56 | }
--------------------------------------------------------------------------------
/CreatePrComment/tests/L0.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert'
2 | import * as sinon from 'sinon'
3 | import rewiremock from 'rewiremock'
4 | import { should as Should, expect } from 'chai'
5 | import { IGitApi, GitApi } from 'azure-devops-node-api/GitApi'
6 | import * as GitInterfaces from 'azure-devops-node-api/interfaces/GitInterfaces'
7 |
8 | var should = Should()
9 |
10 | const debugMessages: string[] = []
11 | let variables: {[key: string]: string} = {}
12 | const inputs: {[key: string]: string} = {}
13 |
14 | rewiremock('azure-pipelines-task-lib')
15 | .with({
16 | debug: sinon.stub().callsFake(m => debugMessages.push(m)),
17 | getInput: sinon.stub().callsFake(i => { return inputs[i] || null }),
18 | getVariable: sinon.stub().callsFake(v => { return variables[v] || null }),
19 | getEndpointAuthorizationParameter: () => 'fooPAT',
20 | loc: sinon.stub().returnsArg(0)
21 | })
22 |
23 | rewiremock.enable()
24 |
25 | import { IClientFactory, CreatePRCommentTask } from '../src/task'
26 |
27 | class ClientFactoryMock implements IClientFactory {
28 | called: boolean = false
29 | createdCommentThread: GitInterfaces.GitPullRequestCommentThread = {}
30 | createdRepositoryId: string = ''
31 | createdPullRequestId: number = 0
32 | expectedThreads: GitInterfaces.GitPullRequestCommentThread[] = []
33 |
34 | public async create (): Promise {
35 | const gitApiStub = {
36 | getThreads: async (repositoryId: string, pullRequestId: number, project?: string, iteration?: number, baseIteration?: number): Promise => {
37 | return await new Promise(
38 | (resolve: (value: GitInterfaces.GitPullRequestCommentThread[]) => void, reject: (reason?: any) => void) => {
39 | resolve(this.expectedThreads)
40 | })
41 | },
42 | createThread: async (commentThread: GitInterfaces.GitPullRequestCommentThread, repositoryId: string, pullRequestId: number, project?: string): Promise => {
43 | return await new Promise(
44 | (resolve: (value: GitInterfaces.GitPullRequestCommentThread) => void, reject: (reason?: any) => void) => {
45 | this.createdCommentThread = commentThread
46 | this.createdRepositoryId = repositoryId
47 | this.createdPullRequestId = pullRequestId
48 | this.called = true
49 | resolve(undefined!) // currently not used.
50 | })
51 | }
52 | }
53 | return gitApiStub
54 | }
55 | }
56 |
57 | describe('CreatePRCommentTaskV0 Tests', function () {
58 | it('run all inputs function', async () => {
59 | const factoryMock: IClientFactory = new ClientFactoryMock()
60 | variables['Build.Repository.ID'] = '3'
61 | variables['System.PullRequest.PullRequestId'] = '4'
62 | inputs.AzureDevOpsService = 'devopspat'
63 | inputs.Comment = 'foo'
64 |
65 | const commentTask = new CreatePRCommentTask(factoryMock)
66 | await commentTask.run();
67 |
68 | (factoryMock as ClientFactoryMock).called.should.be.true
69 | })
70 |
71 | it('run substitution', async () => {
72 | const factoryMock: IClientFactory = new ClientFactoryMock()
73 | variables['Build.Repository.ID'] = '3'
74 | variables['System.PullRequest.PullRequestId'] = '4'
75 | variables.Bar = 'bar'
76 | inputs.AzureDevOpsService = 'devopspat'
77 | inputs.Comment = 'foo, $(Bar)'
78 |
79 | const commentTask = new CreatePRCommentTask(factoryMock)
80 | await commentTask.run()
81 | const comments = (factoryMock as ClientFactoryMock).createdCommentThread.comments
82 | if (comments !== undefined) {
83 | const content = comments[0].content
84 | if (content !== undefined) {
85 | content.should.be.equal('foo, bar')
86 | } else {
87 | assert.fail('content is undefined')
88 | }
89 | } else {
90 | assert.fail('comments is undefined')
91 | }
92 | })
93 |
94 | it('ignored if it is non-pullrequest pipeline', async () => {
95 | const factoryMock: IClientFactory = new ClientFactoryMock()
96 | variables = {}
97 | variables['Build.Repository.ID'] = '3'
98 | inputs.AzureDevOpsService = 'devopspat'
99 | inputs.Comment = 'foo'
100 |
101 | const commentTask = new CreatePRCommentTask(factoryMock)
102 | await commentTask.run();
103 |
104 | (factoryMock as ClientFactoryMock).called.should.be.false
105 | })
106 |
107 | it('suppress the comment if there is already created', async () => {
108 | const factoryMock: IClientFactory = new ClientFactoryMock()
109 | variables['Build.Repository.ID'] = '3'
110 | variables['System.PullRequest.PullRequestId'] = '4'
111 | inputs.AzureDevOpsService = 'devopspat'
112 | inputs.Comment = 'foo'
113 | const commentObject = {
114 | content: 'foo'
115 | }
116 | const thread: GitInterfaces.GitPullRequestCommentThread = {
117 | comments: [
118 | commentObject
119 | ]
120 | };
121 | (factoryMock as ClientFactoryMock).expectedThreads = [thread]
122 |
123 | const commentTask = new CreatePRCommentTask(factoryMock)
124 | await commentTask.run();
125 |
126 | (factoryMock as ClientFactoryMock).called.should.be.false
127 | })
128 | })
129 |
130 | rewiremock.disable()
131 |
--------------------------------------------------------------------------------
/CreatePrComment/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | // "incremental": true, /* Enable incremental compilation */
5 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
7 | // "lib": [], /* Specify library files to be included in the compilation. */
8 | // "allowJs": true, /* Allow javascript files to be compiled. */
9 | // "checkJs": true, /* Report errors in .js files. */
10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
13 | "sourceMap": false, /* Generates corresponding '.map' file. */
14 | // "outFile": "./", /* Concatenate and emit output to single file. */
15 | "outDir": ".bin/", /* Redirect output structure to the directory. */
16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
17 | // "composite": true, /* Enable project compilation */
18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
19 | // "removeComments": true, /* Do not emit comments to output. */
20 | // "noEmit": true, /* Do not emit outputs. */
21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
24 | /* Strict Type-Checking Options */
25 | "strict": true, /* Enable all strict type-checking options. */
26 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
27 | // "strictNullChecks": true, /* Enable strict null checks. */
28 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
29 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
30 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
31 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
32 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
33 | /* Additional Checks */
34 | // "noUnusedLocals": true, /* Report errors on unused locals. */
35 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
36 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
37 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
38 | /* Module Resolution Options */
39 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
43 | // "typeRoots": [], /* List of folders to include type definitions from. */
44 | // "types": [], /* Type declaration files to be included in compilation. */
45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
46 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
47 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
48 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
49 | /* Source Map Options */
50 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
51 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
54 | /* Experimental Options */
55 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
56 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
57 | "skipLibCheck": true,
58 | "forceConsistentCasingInFileNames": true
59 | }
60 | }
--------------------------------------------------------------------------------
/CreatePrComment/vss-extension-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/CreatePrComment/vss-extension-icon.png
--------------------------------------------------------------------------------
/CreatePrComment/vss-extension.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifestVersion": 1,
3 | "id": "create-pr-comment-task",
4 | "name": "Create Pull Request Comment",
5 | "publisher": "CSE-DevOps",
6 | "version": "0.1.0",
7 | "galleryFlags": [
8 | "Public",
9 | "Preview"
10 | ],
11 | "targets": [
12 | {
13 | "id": "Microsoft.VisualStudio.Services"
14 | }
15 | ],
16 | "description": "Create Pull Request Comment",
17 | "categories": [
18 | "Azure Pipelines"
19 | ],
20 | "tags": [
21 | "Pull Request",
22 | "PR",
23 | "PR Comment",
24 | "Utility task",
25 | "Utility",
26 | "Azure Pipelines"
27 | ],
28 | "content": {
29 | "details": {
30 | "path": "README.md"
31 | },
32 | "license": {
33 | "path": "LICENSE.md"
34 | }
35 | },
36 | "icons": {
37 | "default": "vss-extension-icon.png"
38 | },
39 | "files": [
40 | {
41 | "path": "task"
42 | }
43 | ],
44 | "links": {
45 | "home": {
46 | "uri": "https://github.com/microsoft/CSEDevOps/tree/main/CreatePrComment"
47 | },
48 | "getstarted": {
49 | "uri": "https://github.com/microsoft/CSEDevOps/blob/main/CreatePrComment/README.md"
50 | },
51 | "learn": {
52 | "uri": "https://github.com/microsoft/CSEDevOps/blob/main/CreatePrComment/README.md"
53 | },
54 | "support": {
55 | "uri": "https://github.com/microsoft/CSEDevOps/discussions/categories/createprcomment"
56 | },
57 | "repository": {
58 | "uri": "https://github.com/microsoft/CSEDevOps/tree/main/CreatePrComment"
59 | },
60 | "issues": {
61 | "uri": "https://github.com/microsoft/CSEDevOps/issues?q=is:issue+label:CreatePrComment"
62 | },
63 | "license": {
64 | "uri": "https://github.com/microsoft/CSEDevOps/blob/main/CreatePrComment/LICENSE.md"
65 | }
66 | },
67 | "repository": {
68 | "type": "git",
69 | "uri": "https://github.com/microsoft/CSEDevOps"
70 | },
71 | "contributions": [
72 | {
73 | "id": "service-endpoint",
74 | "description": "Service Endpoint type for Azure DevOps PAT",
75 | "type": "ms.vss-endpoint.service-endpoint-type",
76 | "targets": [
77 | "ms.vss-endpoint.endpoint-types"
78 | ],
79 | "properties": {
80 | "name": "azuredevops",
81 | "displayName": "CreatePRCommentTaskV0 Azure DevOps PAT",
82 | "config": {
83 | "type": "string",
84 | "description": "Put your Personal Access Token of the Azure DevOps",
85 | "required": true
86 | },
87 | "authenticationSchemes": [
88 | {
89 | "type": "ms.vss-endpoint.endpoint-auth-scheme-none",
90 | "inputDescriptors": [
91 | {
92 | "id": "pat",
93 | "name": "PAT",
94 | "description": "Azure DevOps Personal Access Token here.",
95 | "inputMode": "passwordbox",
96 | "isConfidential": true,
97 | "validation": {
98 | "isRequired": true,
99 | "dataType": "string"
100 | }
101 | }
102 | ]
103 | }
104 | ],
105 | "helpMarkDown": "Create a Pull Request Comment."
106 | }
107 | },
108 | {
109 | "id": "custom-build-release-task",
110 | "type": "ms.vss-distributed-task.task",
111 | "targets": [
112 | "ms.vss-distributed-task.tasks"
113 | ],
114 | "properties": {
115 | "name": "task"
116 | }
117 | }
118 | ]
119 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/PipelineRunnerExtension/README.MD:
--------------------------------------------------------------------------------
1 | # Configurable Pipeline Runner
2 |
3 | The purpose of this Build task is to provide a solution for :
4 |
5 | - Triggering Remote/Delegate/Child Pipelines any amount of times from a Parent pipeline.
6 | - The build task allows passing variables that are passed to the delegate pipeline at runtime.
7 | - The variable array in Json Array format will determine how many delegate pipeline instances are started (Restricted to the Pipeline Agents Available)
8 | - Monitor the execution of the remotely triggered delegate pipelines by polling execution status on a configurable interval before completing.
9 | - Downloads any Artifacts Produced by the remotely triggered delegate pipelines and publishes it to the Parent Pipeline.
10 | - Creates a summary page with links to the pipeline execution results for easy reference.
11 |
12 | ## Getting Started
13 |
14 | The Build Task requires the configuration of an Azure DevOps service connection. The respective service connection will need personal access token with multiple organization access. You can read more about creating your [Personal Access Token](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=vsts) here,
15 |
16 | Why is a PAT required to configure the task ?
17 | Answer: The PAT is used at design time to query the Azure DevOps REST APIs for the configure Service Connection in order to return filtered lists containing relevant Projects,Folders (in which build definitions are nested) and the Build Definitions.
18 |
19 | At runtime the Build Task uses the Azure Pipelines CLI to trigger and monitor pipelines and uses the SystemAccessToken for authentication.
20 |
21 | 1. Create a Service Connection
22 |
23 | 
24 |
25 | 2. Configure Build Task Properties/Fields
26 |
27 | Configure the task with the required properties to trigger your builds. The Pipeline task can be added to you Parent Pipeline multiple times but will require that you set a unique CustomPrefix per instance in order to correctly separate build artifacts and summary reports as depicted in the following numbered list.
28 |
29 | 
30 |
31 | 3. Review Pipeline Summary Report
32 |
33 | For each instance of the Build Task that is configured a Summary Report will be displayed using the CustomPrefix provided during the task configuration. It will list the builds that it executed as well as it's final status, either **Succeeded/Failed** with a clickable link to the build results page for easy referencing.
34 |
35 | 
36 |
37 | 4. Review Artifacts
38 |
39 | If the triggered builds produce artifacts and you have configured the task to download artifacts the artifacts will be consolidated in a single published artifact, organized by BuildID of the delegate builds. If the task is added to your Parent Pipeline multiple times, the artifacts will be published using the unique CustomPrefix that you have configured per instance as a grouping mechanism.
40 |
41 | 
42 |
43 | ## Task Fields
44 |
45 | This section describes the fields that can be configured on the Build Task
46 |
47 | Inputs:
48 |
49 | - **Service Connection**: Pick List which lists service connections created as in **step 1** above
50 | - **Project**: Project in which the Build Definitions can be found
51 | - **Folder Path**: Folder location where Build Definitions are stored as an organizational unit
52 | - **Build Definition**: The Name of the Build that you wish to trigger
53 | - **Set Branch**: Boolean property which enables a field that allows you to pick define a branch against which the triggered pipeline must run. If not selected the pipeline will be run against the main branch
54 | - **Branch**: Set the branch that the triggered pipeline must target. If left blank defaults to main
55 | - **Parameter Source**: 'inline'/ 'filepath' allows build parameters to be passed from a file or inline
56 | - **Build Variables**: Sample Json Array '[{"Var1":"hello","Var2":"world" },{"Var1":"Bye","Var2":"world" }]' in the preceding example the delegate pipeline will be executed twice one for each **element {}** and in each case the variables **Var1** and **Var2** will be passed to the Pipeline being executed as variables
57 | - **Custom Prefix**: Must be set to a unique string value if the Build Task is added to Parent Pipeline more than once
58 | - **Wait Till Triggered Builds Finish**: Boolean value to determine weather a fan-out and fan in pattern is observed. If left unchecked delegate pipelines will be Triggered, but the parent pipeline will not wait for completion and will not collect artifacts or report status of the resultant builds.
59 | - **Build Completion Polling Interval**: The sleep time between status checks set in seconds
60 | - **Build Timeout in minutes**: Time to wait before abandoning polling of queued builds.
61 | - **Download Build Artifacts**: Boolean field to opt into downloading build Artifacts
62 | - **Artifact Drop Directory**: Defaults to '$(Build.ArtifactStagingDirectory)' and is the directory where build artifacts will be stored temporarily
63 | - **Store Triggered Build Ids In A Variable**: boolean value to determine whether an environment variable with the following naming convention ${CUSTOMPREFIX}_BUILD_IDS or TRIGGERED_BUILD_IDS where a CustomPrefix has not been provided and there is only a single instance of the build task in the Parent pipeline.
64 |
--------------------------------------------------------------------------------
/PipelineRunnerExtension/images/connection-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/PipelineRunnerExtension/images/connection-icon.png
--------------------------------------------------------------------------------
/PipelineRunnerExtension/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/PipelineRunnerExtension/images/icon.png
--------------------------------------------------------------------------------
/PipelineRunnerExtension/images/screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/PipelineRunnerExtension/images/screenshot-1.png
--------------------------------------------------------------------------------
/PipelineRunnerExtension/images/screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/PipelineRunnerExtension/images/screenshot-2.png
--------------------------------------------------------------------------------
/PipelineRunnerExtension/images/screenshot-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/PipelineRunnerExtension/images/screenshot-3.png
--------------------------------------------------------------------------------
/PipelineRunnerExtension/images/screenshot-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/PipelineRunnerExtension/images/screenshot-4.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CSE DevOps Extensions
2 |
3 | ## Available Extensions
4 |
5 | - [Create Pull Request Comment](./CreatePrComment)
6 | - [OWASP ZAP Scanner](./Zap)
7 | - [Configurable Pipeline Runner](./PipelineRunnerExtension)
8 |
9 | ## Getting Help
10 |
11 | Please see the guidance provided in the documentation for each perspective extension linked above as well as the Marketplace. Please submit issues with a descriptions of problems you encounter while using these extension.
12 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Security
4 |
5 | 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 [many more](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition]() of a security vulnerability, please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
14 |
15 | 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 the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
16 |
17 | 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).
18 |
19 | 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:
20 |
21 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | - Full paths of source file(s) related to the manifestation of the issue
23 | - The location of the affected source code (tag/branch/commit or direct URL)
24 | - Any special configuration required to reproduce the issue
25 | - Step-by-step instructions to reproduce the issue
26 | - Proof-of-concept or exploit code (if possible)
27 | - Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | 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.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Zap/README.md:
--------------------------------------------------------------------------------
1 | # OWASP/ZAP Scanning extension for Azure DevOps
2 |
3 | 
4 |
5 | [OWASP/ZAP](https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project) is a popular free security tool for helping to identify vulnerabilities during the development process from [OWASP](https://www.owasp.org). This extension shifts scanning and reporting into the Azure DevOps Pipeline model to enable quick feedback and response from development teams throughout the development life-cycle.
6 |
7 | 
8 |
9 | ## Usage
10 |
11 | ### Prerequisite
12 |
13 | This task simplifies shifting security scanning of web applications into the DevOps pipeline in part by removing the requirement of having a running, exposed ZAP proxy before attempting the scan. By installing the proxy, you are enabling self-contained scans within your CI/CD pipeline. The core requirement for usage is a Docker install available to this task which supports running Linux containers. The operating system of the host is otherwise irrelevant. When this requirement is not met, the task will fail with an error message of `image operating system "linux" cannot be used on this platform`.
14 |
15 | ### Configuration
16 |
17 | After installing the scanner from the Azure DevOps Marketplace, you will need to add the scanner to your agent job and configure a few basic requirements.
18 |
19 | 
20 |
21 | - The "Display name" of the task can be left as-is, or it can be updated to fit withing the naming conventions of your pipeline better.
22 |
23 | - By default, the task will run a baseline scan.
24 |
25 | - Custom configuration for ZAP may be provided through a "context file". This is ZAP functionality, and [is documented by the official project team](https://www.zaproxy.org/docs/desktop/start/features/contexts/).
26 |
27 | #### Baseline Scan Notes
28 |
29 | > The baseline scan will spider the target for 1 minute and then wait for the passive scanning to complete. This makes for a relatively short-running scan that doesn't perform any attacks.
30 |
31 | #### Full Scan Notes
32 |
33 | > A full-scan can be run by ticking the "Aggressive Scan Mode" checkbox. This scan doesn't have a time limit and does perform 'attacks.' It can run for a long time. Not ideally suited for CI, but is a useful tool for release-gates. 
34 |
35 | - The "Failure Threshold" indicates the score at which the pipeline will begin to fail. The scoring mechanism built into this scanner is meant to be suggestive, and security personnel knowledgable about threat-models for the specific application should be engaged to adjust this value appropriately. The default of 50 is only a starting point for incorporating the scanner and is not likely to be what works for your application! The ZAP scanner generates an HTML report (available in the Az DevOps build artifacts) that contains one entry per vulnerability discovered along with a confidence rating without any grouping. This approach does not provide a useful story from a developer perspective. The threshold sets out to provide a handy reference for improving the development workflow when considering vulnerability scanning. This limit is an aggregate score that first groups like vulnerabilities before applying the confidences, and total times a type of vulnerability appears to score the scan.
36 |
37 | - By default, that "Scan Type" used is "Scan on Agent." This type of scan is beneficial in pipelines for containerized applications. **This usage requires a preceding step to build your image and run it (detached) within the agent.**
38 |
39 | - When running a full-scan in release-pipelines, or if your application is not containerized and has to run in a VM, "Scan Type" can be changed to "Targeted Scan." In this case, you are required to provide the schema and address for your target. Ex. or .
40 |
41 | > - By default, the scan will be performed without much effort at scoping for tech, excluded URL endpoints, etc.., but you can provide a context file for a more focused scan. The context file is useful to take full advantage of the baseline scans minute of crawling or, to narrow the scope of a full-scan in aggressive mode to keep the duration as short as possible. To accomplish this, check the "Provide Context File" box and provide the path to a context file in your source repository relative to the build copy of the source. 
42 |
43 | - ZAP contexts and context files are not within the scope of our documentation. [Please refer to ZAP's official documentation, here.](https://www.zaproxy.org/docs/desktop/start/features/contexts/)
44 |
45 | - Finally, provide an optional port number for custom ports. By default, the scan will be interested in port 80 on the target system.
46 |
47 | ### Reporting Options
48 |
49 | The extension generates 2 reports (report.html & report.json) that both contain the details of the results. These reports are in a directory "owaspzap" once the scanner completes. To be useful the report can be staged and published. For example the following tasks will attach these raw output files as build artifacts.
50 |
51 | The ZAP scanner includes several reporting options. None of which are optimal for use by development teams. However, we do provide several of these (HTML & JSON) as downloadable artifacts in the build. To make the scans more useful for development teams and get real feedback into the pipeline, we want a more helpful report of the scan results. Above, we discussed generating a threshold for scoring and failing a CI build. Below are some path(s) to reporting. The goal here is providing a flexible extension that can be reported on as appropriate for your project/team with some possible paths on getting even more value from the extensions.
52 |
53 | #### Publish Reports as Build Artifacts
54 |
55 | If you would like to publish the generated html & json reports as build artifacts you can use the copy and publish tasks available in Azure Devops!
56 |
57 | ``` bash
58 | - task: CopyFiles@2
59 | condition: always()
60 | inputs:
61 | SourceFolder: 'owaspzap/'
62 | TargetFolder: '$(Build.ArtifactStagingDirectory)'
63 |
64 | - task: PublishBuildArtifacts@1
65 | condition: always()
66 | inputs:
67 | ArtifactName: 'owasp_zap_reports'
68 | ```
69 |
70 | #### Install handlebars
71 |
72 | Additionally, you can take advantage of handlebars templating for a simple reporting dashboard/tab and generate the template to report from.
73 |
74 | ```YAML
75 | - bash: |
76 | sudo npm install -g handlebars-cmd
77 |
78 | cat < owaspzap/nunit-template.hbs
79 |
80 |
84 | {{#each site}}
90 |
91 |
92 | $BUILD_SOURCESDIRECTORY/owaspzap/report.html
93 |
94 |
95 | {{#each alerts}}
101 |
102 |
103 |
104 |
105 |
106 |
118 |
119 |
120 |
121 | {{/each}}
122 |
123 | {{/each}}
124 |
125 | EOF
126 | displayName: 'owasp nunit template'
127 | condition: always()
128 | ```
129 |
130 | #### Report Generation
131 |
132 | Use the handlebars template and json report from the ZAP scan to generate an xml report in the nunit style to display.
133 |
134 | ```YAML
135 | - bash: ' handlebars owaspzap/report.json < owaspzap/nunit-template.hbs > owaspzap/test-results.xml'
136 | displayName: 'generate nunit type file'
137 | condition: always()
138 | ```
139 |
140 | #### Publish Report (Nunit Style)
141 |
142 | ```YAML
143 | - task: PublishTestResults@2
144 | displayName: 'Publish Test Results **/TEST-*.xml'
145 | inputs:
146 | testResultsFormat: NUnit
147 | testResultsFiles: 'owaspzap/test-results.xml'
148 | condition: always()
149 | ```
150 |
151 | You should now have 'test' tab on the pipeline build that displays useful infomration about vulnerabilities revealed during the scan. The vulnerability test results are not reliant on the pass/fail outcome of the build. This means that even with a scan that is acceptable by the threshold you could have some areas of concern to explore in the repot.
152 |
153 | 
154 |
155 | The list of failures (currently under "NUnit Test Run) are expandable links. Clicking on each will open details to the right which can guide the team to fixing the issues found.
156 |
157 | 
158 |
159 | #### Other Reporting Options
160 |
161 | Other reporting options will be added to the extension. The HTML & JSON report will remain and offer the flexibility for parsing and publishing customer reports as above using the approach that works best for your team and project.
162 |
--------------------------------------------------------------------------------
/Zap/docs/images/scan-results-collapsed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/Zap/docs/images/scan-results-collapsed.png
--------------------------------------------------------------------------------
/Zap/docs/images/scan-results-drill-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/Zap/docs/images/scan-results-drill-down.png
--------------------------------------------------------------------------------
/Zap/docs/images/zap-aggressive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/Zap/docs/images/zap-aggressive.png
--------------------------------------------------------------------------------
/Zap/docs/images/zap-context-empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/Zap/docs/images/zap-context-empty.png
--------------------------------------------------------------------------------
/Zap/docs/images/zap-context-provided.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/Zap/docs/images/zap-context-provided.png
--------------------------------------------------------------------------------
/Zap/docs/images/zap-scanner-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/Zap/docs/images/zap-scanner-config.png
--------------------------------------------------------------------------------
/Zap/docs/images/zap-scanner-task.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/Zap/docs/images/zap-scanner-task.png
--------------------------------------------------------------------------------
/Zap/docs/images/zap-target.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/CSEDevOps/b25c6b6e45e40bea9204ac3a9cfdebecc8dab4c0/Zap/docs/images/zap-target.png
--------------------------------------------------------------------------------