Approval step on gitlab CI - gitlab

I want to discuss and to know more approaches about how to add a approval step on Gitlab CI
Everybody knows that gitlab doesn't has a approval feature on the pipeline
I did a small template to add a kind of approval step in any step
this is the content of the template
.approval_template:
image: node:buster
before_script:
- |
cat <<'EOF' >> approval.js
var users = process.env.USERS.split(',');
if (users.includes(process.env.GITLAB_USER_LOGIN)) {
console.log(process.env.GITLAB_USER_LOGIN + " allowed to run this job")
process.exit(0)
} else {
console.log(process.env.GITLAB_USER_LOGIN + " cannot trigger this job")
console.log("List of users allowed to run this job")
for (user in users)
{
console.log(users[user])
}
process.exit(1)
}
EOF
- node approval.js
- rm approval.js
when: manual
how does it work?
This template checks if the user who triggered the job is inside the USERS variable (array)
The check happens on before_script block and it works in any step (without overwrite the before_script)
It works very well but I want to know if there is a better approach to do it.

Anyone with write access to the repository can change that file and add themselves to the list. But my concern with the scripted approach is a different one, as you may end up confusing your future self or your successor(s) with a block of code to maintain. The person who triggers a job may be a different person than the one who would review the code, also I'm not sure if you want to assume that triggering equals approval.
Let me explain what I've been doing: Imagine having a development and a master branch - only master gets deployed automatically. Nobody can push directly to master when you protect the branch. Everything has to be merged into masterby using merge requests. Permission-wise it still comes down to having write access (developer or a higher role). This keeps the step of approval in the GUI (where you would expect an approval feature to appear once it's added). Additionally, accepting a merge request will trigger a dedicated e-mail notification.
This works very well when you make sure merging into master is a taboo except for the lead developers. Maybe this is not applicable for your situation but a different approach; and even this one needed my successor to be educated on where to click in order to approve a merge request.
If you want to fast-track the deployment of some developers you can assign them a higher role and use my suggested approach to let them push to the master branch directly. That way they skip the step of awaiting approval.

Related

Gitlab Merge Request When To Approve

So we're having a little bit of a debate where I work as to what point should a reviewer "Approve" a Merge Request.
We have setup some Gitlab Pipelines to run and carry out what I would say are fairly standard stuff (Build, Automated Tests, Sonar etc). Now we're trying to improve best practises across the department and a debate has started as to when the reviewer(s) should actually "Approve" the Merge Request.
We have the 1 argument that says the Pipeline acts as an "Approver" itself and if it fails it basically should prevent the Merge Request from happening, so it doesn't matter when the reviewer(s) hit that "Approve" button, because the reviewer(s) are reviewing the Code and not the pipeline.
And then you have another argument that says the reviewer(s) should not be "Approving" until the Pipeline passes, and they should be dependent on the Pipeline passing as to whether they should be "Approving" the Merge Request or not.
So should the reviewer(s) of a Merge Request wait until the the Gitlab Pipeline finishes and if the pipeline is successful then "Approve" or should they be able to "Approve" once they have reviewed the code, no matter if the pipeline has finished or not?
It's best practice to wait until the pipeline passes, review the code and then do an approval. If the pipeline doesn't pass in the first place, there is an issue and the developer needs to fix his/her PR before someone spends time to review it.

Detect a build requested by pull request and one run by any updates to the PR

I currently have a task that I intend to run only once when a PR is created. Any pipeline runs due to new commits should not trigger the task. I was wondering if there is a way to detect the runs triggered by changes to code in the PR? When I use the predefined variable $(Build.Reason) I get back PullRequest for both builds(One triggered when PR is created and other when updates are made to PR).
This is what I have in my pipeline and I have enabled build validation for my pipeline.
trigger:
- master
pr:
- master
I don't think there's a way to differentiate the "PR is created" and "PR is updated" build reasons based only on the predefined variables.
However, you can choose a different route depending on what this task you should only run once is. If it is something that can be wrapped into a service with a public endpoint, you can try leveraging the Webhooks.
So, if this is an option for you, try the following:
wrap the functionality required to run only on the PR creation into the service with the public endpoint
create a webhook, choose "Pull request created" event type and enter the public URL of your service
As a result, your build logic won't branch depending on the build reason, and that specific action will be run by the webhook.
I understand it all sounds like a hack and unnecessary complexity, but it's up to you to decide whether it fits your case. At least, this is possible technically.

How to run a job after MR is approved, but reject MR, if the job failed?

I would like some automated checks were done after MR is approved, because for those checks pipeline has to access protected variables.
If these checks fail, MR should be rejected.
In other words the desired sequence should be this:
MR created -> build -> run tests -> MR approved (no malicious exposure of protected variables)-> merged to protected branch -> run checks -> rollback on failure.
Is this possible?
You can do this by using the Gitlab API and adding two new jobs at the end of the pipeline.
The when keyword is one of the many ways to control which jobs are executed in a pipeline. Two of the available when options will be useful here. The first job to put at the end of your pipeline will be for the success condition:
approve_merge_request:
stage: approve_merge_request
when: on_success
script:
- # this will call the Gitlab Merge Requests API and approve it. More on this below
This parameter to when is actually the default, so you could leave the when off of this step and it would still work. I added it here for clarity. What it means is that this job will only run if every other job in the pipeline passed. However, if a job fails but has the allow_failure: true attribute, it is still considered a pass and this job will run (there's currently no way to detect that some jobs were allowed to fail in a when condition). In addition, jobs with when: manual that haven't run are considered passed, even though it could later fail. when: manual means the job has to be started by an API call or UI interaction by a user.
The second job will handle our failure condition:
reject_merge_request:
stage: approve_merge_request
when: on_failure
script:
- # this will call the Gitlab Merge Requests API and reject it. More on this below
This parameter to when means that this job will only run if at least one job prior to this has failed, and doesn't have allow_failure: true.
The Merge Requests API can be used to approve, reject, comment on, and merge a Merge Request, among other options. The full documentation is available here: https://docs.gitlab.com/ee/api/merge_requests.html. Unfortunately, the API to use the "approvals" feature of merge requests is available only to paying customers, but you can still get a similar result without the approvals.
You can approve a Merge Request (note, this doesn't merge it, that's "accepting" the merge request. Also, this is a paid feature so is only available to Starter or Bronze customers and above) with the API operation here: https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request. After you approve the Merge Request, you probably want to accept it, which will merge the source branch into the target branch. That operation is outlined below.
You can get all of the required ids from the predefined variables Gitlab CI gives you. The project ID can be retrieved from the variable $CI_PROJECT_ID. The Merge Request IID is different from the Merge Request ID. The "ID" version is a unique ID across your entire Gitlab instance, and the "IID" version is specific to the project it's in. For this operation we need the IID. You can get that with the variable $CI_MERGE_REQUEST_IID. You should check that each variable exists before trying to use it as it will cause issues in your API call. It will exist for all pipelines associated with a Merge Request that is open.
There isn't equivalent functionality in Gitlab Merge Requests to "reject" other than commenting and closing, which I outline below.
If you're not a paid customer, or you want to accept and merge the request, you want to use the Accept Merge Request operation here: https://docs.gitlab.com/ee/api/merge_requests.html#accept-mr. This uses the same variables from above.
Finally, if you're not a paid user but still want to "reject" the merge request, you can use the Notes API to add a comment to the Merge Request. The operation to add a comment to a merge request is here: https://docs.gitlab.com/ee/api/merge_requests.html#accept-mr.
After commenting, if you want to close the merge request, you can do so with the Update MR operation and setting the state_event to close: https://docs.gitlab.com/ee/api/merge_requests.html#update-mr

How can I create some sort of policy for a pull request to be rejected if there are x amount of bug items in the back log

I am trying to find a way to reject a pull request into my master branch if there are still bug work items open in my back log in azure devops. I know I can create pre-deployment gates in the release pipeline to prevent release but I want to stop the build pipeline under that condition. to be more specific im trying to make sure my pull request to start my build to be later be released doesn't contain a critical bug in the back log items
How can I create some sort of policy for a pull request to be rejected if there are x amount of bug items in the back log
AFAIK, there is no such out of box way to do this.
To receive this, you could create a Build validation on the master branch:
Branches->master->Branch policies->Build validation
Then, create a build pipeline with Agentless job:
and select Shared Queries, set the Upper threshold for the Query work items task:
Now, we just need to create a Shared Query to get the all the open bug work items:
Hope this helps.

securing a Google Apps Script linked to an authorized trigger so others can edit

I am pretty sure my understanding is correct but since I cannot find any Google documentation that explicitly highlights this I wanted to ask here.
Per https://developers.google.com/apps-script/guides/triggers/installable:
Installable triggers always run under the account of the person who created them.
And we know that when you create a trigger it will ask to authorize for all the scopes the script uses.
Then, that means that anyone with edit access to the script could leverage the Google identity of the user used to create the trigger to access the scopes the trigger is authorized for.
For example:
User 1 creates a Google Apps Script that uses GmailApp to send an e-mail
(i.e. GmailApp.sendEmail("one#example.com", "test subject", "email body");)
User 1 creates a trigger to run said script every hour and authorizes it with the appropriate GmailApp scopes
User 1 gives User 2 edit access to said script
Now, User 2 can go into said script and make changes to the code and access User 1's Gmail account. For example, user 2 could change the code to:
var emails = GmailApp.search("search string to find sensitive emails")
// use GmailApp.sendEmail to forward those details to someone else like User 2
All they would have to do is make changes to the code and save; they wouldn't need to re-create the trigger since it already exists. And the next time the trigger runs it would run the newer/updated code.
I was able to confirm this behavior by creating a test script on one of my accounts and giving another account edit access.
So my question is, what is the official/recommended way to mitigate this risk? The obvious answer is to not give anyone else edit access but what if that is not an option -- what if for support purposes multiple people need to be able to access the script, then what?
As you say, the only official/recommend way is to limit editing access to trusted persons.
In your particular example, User 1 could have chosen MailApp instead of GmailApp. The two seemingly redundant services are available separately because MailApp has very limited privledges exposed compared to GmailApp. (For instance, User 2 cannot search the victims Gmail with the MailApp service.)
You can collaborate while avoiding giving direct access to your script file using clasp and git. Only you push with clasp to the script. Everyone else submits changes through git. You can setup the system to be fully automatic (i.e. a git push triggers a clasp push) or manual (i.e. you review all changes first), bit either way you have good records of who did what, when with git.
There's inherent trust when you provide edit access to the script project. You either trust the person or don't trust them. There's no inbetween.
Some "theoretical" ways you may still protect the data:
Create and use different Google accounts.
Install Triggers at the specific deployment/not at Head:
Possible only if done manually. Installable triggers created programmatically can only be used at Head
When you deploy a web-app/api, You can deploy it a specific version.
This deployment version can then be provided, When you create a new trigger for a project here.
There is no need for a working web-app/api. We're only looking to get a deployment id.
In this way, even if user changes the script, your trigger will only run at the old version deployed.
Deployed versions can be seen at Publish> Deploy from manifest.
As the previous answer states, git would be a better call.
For all practical purposes, any data you share with a malicious entity should be considered compromised.

Resources