Free GitHub Actions GH-200 Practice Questions: Secure Automation
Practice 10 free GitHub Actions (GitHub Actions GH-200) questions on Secure Automation, with answers, explanations, and the IT Mastery next step.
Try the IT Mastery web app for a richer interactive practice experience with mixed sets, timed mocks, topic drills, explanations, and progress tracking.
Topic snapshot
| Field | Detail |
|---|---|
| Practice target | GitHub Actions GH-200 |
| Topic area | Secure and Optimize Automation |
| Blueprint weight | 15% |
| Page purpose | Focused sample questions before returning to mixed practice |
How to use this topic drill
Use this page to isolate Secure and Optimize Automation for GitHub Actions GH-200. Work through the 10 questions first, then review the explanations and return to mixed practice in IT Mastery.
| Pass | What to do | What to record |
|---|---|---|
| First attempt | Answer without checking the explanation first. | The fact, rule, calculation, or judgment point that controlled your answer. |
| Review | Read the explanation even when you were correct. | Why the best answer is stronger than the closest distractor. |
| Repair | Repeat only missed or uncertain items after a short break. | The pattern behind misses, not the answer letter. |
| Transfer | Return to mixed practice once the topic feels stable. | Whether the same skill holds up when the topic is no longer obvious. |
Blueprint context: 15% of the practice outline. A focused topic score can overstate readiness if you recognize the pattern too quickly, so use it as repair work before timed mixed sets.
Sample questions
These are original IT Mastery practice questions aligned to this topic area. They are not official GitHub questions, copied live-exam content, or exam dumps. Use them to preview question style and explanation depth before continuing with topic drills, mixed sets, and timed mocks in IT Mastery.
Question 1
Topic: Secure and Optimize Automation
A repository uses deployment environments with these settings:
| Environment | Required reviewers | Secret DEPLOY_URL |
|---|---|---|
staging | none | staging.example |
production | 1 reviewer | prod.example |
There is no repository-level secret named DEPLOY_URL.
A push to main triggers this workflow:
name: release
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "package" > app.txt
- uses: actions/upload-artifact@v4
with:
name: app
path: app.txt
deploy-staging:
needs: build
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/download-artifact@v4
with:
name: app
- run: echo "Deploying to ${{ secrets.DEPLOY_URL }}"
deploy-prod:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- run: echo "Deploying to ${{ secrets.DEPLOY_URL }}"
build and deploy-staging both complete successfully. No one has approved the production environment yet. Which statement matches GitHub Actions behavior?
Options:
A.
deploy-prodstarts immediately, but its secret is blank until approval is granted.B. Both deployment jobs would have required approval because both jobs declare
environment:.C.
deploy-prodis skipped because the uploaded artifact can be downloaded only once.D.
deploy-prodwaits for approval; none of its steps run;deploy-stagingused the staging environment secret.
Best answer: D
Explanation: Environment protections apply to the specific environment a job targets. Here, deploy-staging can run normally and use staging secrets, while deploy-prod pauses before any steps start because production requires reviewer approval.
In GitHub Actions, needs controls job order, and environment protection rules control whether a specific deployment job can start. After build succeeds, deploy-staging runs because the staging environment has no required reviewers. Its DEPLOY_URL value comes from the staging environment secret, since no repository-level secret with that name exists.
When deploy-prod becomes eligible, GitHub checks the production environment rules first. Because approval is required, the job waits and is not dispatched to a runner yet. That means no deploy-prod steps execute and its environment secret is not exposed before approval. Artifacts uploaded in one job can be downloaded by later jobs in the same workflow run, so the earlier artifact step does not block this behavior.
The key point is that protection is per environment, not for every job that uses environment:.
- Empty secret first is wrong because a protected-environment job does not begin running steps before approval.
- Artifact consumed once is wrong because workflow artifacts can be downloaded by multiple later jobs in the same run.
- All environments protected is wrong because only environments with configured protection rules require approval.
Question 2
Topic: Secure and Optimize Automation
A service repository deploys to production from main with this workflow:
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: ./build.sh
deploy-prod:
needs: build
runs-on: ubuntu-latest
steps:
- run: ./deploy-prod.sh
Builds must continue automatically on every push. The deploy-prod job must pause until a member of the ops-reviewers team approves the production deployment. Which configuration best meets this requirement?
Options:
A. Create a
productionenvironment withops-reviewersas required reviewers, and setenvironment: productionondeploy-prod.B. Add an
ifcondition so only specific usernames can rundeploy-prod.C. Replace
pushwithworkflow_dispatch, and allow only ops engineers to start the workflow.D. Protect
mainwith required pull request reviews, and keepdeploy-prodrunning automatically after merges tomain.
Best answer: A
Explanation: Use a GitHub Actions environment for production and add required reviewers to that environment. When the deploy job references that environment, GitHub pauses the job until an authorized reviewer approves it, which directly satisfies the requirement.
Environment protection rules are the built-in way to require human approval before a sensitive job deploys. Configure a production environment in the repository, add the ops-reviewers team as required reviewers, and reference that environment from the deploy-prod job.
deploy-prod:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- run: ./deploy-prod.sh
When the workflow reaches that job, GitHub holds the deployment until an allowed reviewer approves the environment. This preserves automatic build execution on every push while adding a deployment-specific approval gate. Branch protection, manual triggers, and username checks can restrict code changes or who starts a run, but they do not provide the same built-in pre-deployment reviewer control.
- The option using pull request reviews protects merges to
main, but it does not require approval for each production deployment job. - The option switching to
workflow_dispatchchanges how runs start and removes the automatic push flow without adding an environment approval gate. - The option using an
ifcondition only controls job logic based on identity and does not create an auditable reviewer approval step.
Question 3
Topic: Secure and Optimize Automation
An organization wants teams to keep using currently approved GitHub Actions. If a pull request adds a new third-party or unverified action, or changes an existing uses: reference, the security team must review it before the workflow can be used on the protected main branch. Which configuration best meets this requirement?
Options:
A. Use first-time contributor workflow approvals and keep the repository’s default action policy
B. Use an org Actions allowlist with full-SHA pinning and CODEOWNERS review on workflow files
C. Use environment required reviewers for all jobs and allow third-party actions pinned to tags
D. Use restricted self-hosted runners for all workflows and set
GITHUB_TOKENpermissions to read-only
Best answer: B
Explanation: The best fit is to combine governance at the policy layer with review at the pull request layer. An Actions allowlist limits which actions can run, and required CODEOWNERS review on workflow files makes the security team approve any newly introduced or changed action reference before it reaches main.
GitHub Actions does not give trusted maintainers a general runtime prompt to approve each newly added marketplace action. The durable approach is to control both what can run and who must review the change that introduces it. Use an organization Actions policy to allow only approved actions or reusable workflows, and require full commit SHA pinning for integrity. Then protect workflow files with a ruleset or branch protection that requires CODEOWNERS review by the security team. That way, a pull request that adds or changes a uses: reference cannot land on main without review, and even after approval only allowlisted, immutable action versions can run. Environment approvals, fork-run approvals, and runner isolation address different risks but do not create an approval flow for newly introduced actions.
- Environment reviewers gate jobs tied to an environment, typically deployments, but they do not approve new action references in workflow files; tag pinning also allows ref drift.
- First-time contributor approvals apply to untrusted fork-originated workflow runs, not to trusted maintainers changing workflows in the repository.
- Runner isolation and token scoping can reduce impact if a workflow is abused, but they do not require security review before a new action is introduced.
Question 4
Topic: Secure and Optimize Automation
Your repository is configured to retain workflow logs and artifacts for 30 days. A nightly matrix run failed 5 days ago. The run is still visible in Actions, and its job logs open normally, but each failure-bundle artifact can no longer be downloaded.
Exhibit:
- name: Upload failure bundle
if: failure()
uses: actions/upload-artifact@v4
with:
name: failure-bundle-${{ matrix.os }}
path: out/debug/
retention-days: 3
What is the most likely cause?
Options:
A. Matrix artifact links broke when the job names changed
B. The
if: failure()condition prevented post-run artifact retentionC. A shorter artifact retention overrode the repository default
D. The hosted runner deleted the stored artifact at teardown
Best answer: C
Explanation: actions/upload-artifact can set retention for a specific artifact shorter than the repository default. Here, the artifact was uploaded with retention-days: 3, so it expired before the 30-day run logs did.
The key concept is that artifact retention can be configured per upload, while workflow logs follow the repository or organization retention setting. In this run, the repository keeps logs and artifacts for up to 30 days by default, but the failure-bundle upload explicitly set retention-days: 3. That means the artifact can expire after 3 days even though the run record and logs remain available.
if: failure() only controls whether the upload step runs after a failed job; it does not shorten retention. Matrix job naming also does not invalidate stored artifacts from an existing run, and hosted runner teardown does not remove artifacts that were already uploaded to GitHub storage.
When failed-run evidence disappears sooner than expected, inspect the upload-artifact step first for a shorter retention-days value.
- The option about
if: failure()confuses step execution with retention; it decides when the upload happens, not how long GitHub stores it. - The option about matrix job names is not a retention issue; existing artifacts stay tied to their original run even if later runs use different names.
- The option about runner teardown is incorrect because uploaded artifacts are stored by GitHub, not kept on the ephemeral hosted runner after the job ends.
Question 5
Topic: Secure and Optimize Automation
You add artifact attestations to a release workflow. The build job succeeds, but the production deploy job is blocked by an organization policy. The repository uses a shared self-hosted runner group managed outside the release team.
jobs:
build:
runs-on: [self-hosted, linux, shared]
permissions:
contents: read
id-token: write
attestations: write
steps:
- uses: actions/checkout@v4
- run: ./scripts/build.sh
- uses: actions/attest-build-provenance@v3
with:
subject-path: dist/app.tgz
Verification: attestation valid
Policy: blocked
Reason: builder environment not trusted
What is the best explanation for the block?
Options:
A. The build job also needs
contents: writepermission.B.
actions/checkoutmust be pinned to a full SHA.C. The attestation is valid, but the shared self-hosted runner is not trusted.
D.
subject-pathmust reference an uploaded workflow artifact.
Best answer: C
Explanation: The attestation is valid, so the artifact can be tied to the recorded workflow run. But provenance is only as trustworthy as the builder, and a shared self-hosted runner outside the team’s control can still undermine the build, so the policy blocks deployment.
A GitHub artifact attestation can prove that dist/app.tgz came from the recorded workflow run, and the log confirms that provenance verification passed. But that assurance depends on trusting the workflow, its dependencies, and the runner environment. Because this job ran on a shared self-hosted runner managed outside the release team, the attestation cannot by itself prove the build environment was safe from tampering.
- Attestations answer what produced an artifact.
- They do not guarantee the builder was trustworthy.
- Shared self-hosted runners expand the trust boundary.
Pinning actions is also a good hardening step, but the immediate reason for the block is the untrusted runner environment named in the policy result.
contents: writeis not the issue here; the attestation already verified, and the policy message points to builder trust.- Pinning
actions/checkoutto a full SHA improves supply-chain integrity, but it is not required for attestation verification and does not match the stated block reason. subject-pathcan point to a file created in the workspace; it does not have to be an uploaded workflow artifact.
Question 6
Topic: Secure and Optimize Automation
A team wants broad test feedback without running unnecessary Windows jobs or unlimited parallel work. A push triggers this workflow. precheck succeeds. In the test matrix, only windows-latest with Node 20 fails; every other matrix job succeeds.
name: ci
on: [push]
jobs:
precheck:
runs-on: ubuntu-latest
steps:
- run: echo "lint passed"
test:
needs: precheck
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 2
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20]
include:
- os: ubuntu-latest
node: 22
exclude:
- os: windows-latest
node: 18
steps:
- run: echo "test ${{ matrix.os }} / ${{ matrix.node }}"
- if: ${{ matrix.os == 'windows-latest' && matrix.node == 20 }}
run: exit 1
package:
needs: test
runs-on: ubuntu-latest
steps:
- run: echo "package"
Which statement matches GitHub Actions behavior for this run?
Options:
A. Four
testjobs are created; GitHub-hosted runners ignoremax-parallel, so all four can start together;packageruns if any one matrix copy succeeds.B. Five
testjobs are created; theincludeentry duplicates all Ubuntu combinations;packageis canceled immediately when the Windows job fails.C. Three
testjobs are created;packageruns after the first two succeed becausefail-fast: falseallows partial success.D. Four
testjobs are created; at most two run at once;packageis skipped after the matrix completes because onetestjob failed.
Best answer: D
Explanation: The matrix expands to four jobs: Ubuntu 18, Ubuntu 20, Windows 20, and the added Ubuntu 22. max-parallel: 2 caps concurrent matrix jobs, fail-fast: false keeps the remaining matrix jobs running after the Windows failure, and package is skipped because its needs dependency did not fully succeed.
This workflow uses matrix shaping and concurrency controls to balance feedback and cost. Start with the Cartesian product of os and node: 4 combinations. The exclude entry removes windows-latest with Node 18, leaving 3. The include entry then adds one extra combination, Ubuntu with Node 22, for a total of 4 matrix jobs.
precheckruns first becausetestdepends on it.- After
prechecksucceeds, no more than 2testmatrix jobs can run at the same time because ofmax-parallel: 2. - When
windows-latestwith Node 20 fails, the other matrix jobs continue becausefail-fastisfalse. packagewaits for the fulltestmatrix and is skipped because one matrix copy failed.
The key trap is confusing fail-fast: false with downstream partial success; it only stops sibling cancellation.
- Partial success myth
fail-fast: falsekeeps remaining matrix jobs running, but it does not let a downstreamneedsjob start after only some copies succeed. - Wrong matrix count
includeadds one explicit combination; it does not create another cross-product of Ubuntu values. - Parallelism confusion GitHub-hosted runner capacity does not override
max-parallel; the matrix still runs only up to two jobs concurrently.
Question 7
Topic: Secure and Optimize Automation
A repository has a production environment with required reviewers. The deploy job authenticates to the cloud by OIDC and does not need repository write access. The team wants tests on every push and pull request, but production deployment must run only after test succeeds on a push to main. Pull requests must not trigger a production approval request.
name: ci-cd
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./test.sh
deploy:
needs: test
runs-on: ubuntu-latest
environment: production
permissions: write-all
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
Which edit best meets these requirements?
Options:
A. Keep the triggers, but put the branch check on the deploy step and leave
permissions: write-all.B. Add
if: github.event_name == 'push' && github.ref == 'refs/heads/main'todeploy, and setpermissionstocontents: readandid-token: write.C. Change the workflow trigger to only
pushonmain, and keep the deploy job as written.D. Remove
environment: productionand rely on a branch condition indeployto protect releases.
Best answer: B
Explanation: The safest fix is to gate the entire deploy job with a job-level if for push to main and reduce token permissions to only what deployment needs. That keeps PRs running tests without ever entering the protected production environment or keeping unnecessary write access.
Environment protection rules apply when a job targets an environment, so the important control is to stop the deploy job from starting unless the event is a push to main. Because deploy already has needs: test, it will still wait for tests to pass first; once the job is eligible, the production environment can enforce required reviewers. Reducing permissions from write-all to contents: read plus id-token: write follows least privilege for a job that only checks out code and obtains cloud credentials through OIDC.
- Use a job-level
ifto block PR deployments entirely. - Keep
environment: productionso approvals still protect real deployments. - Grant only the token scopes the job actually needs.
A step-level condition is too late, because the job would still target the protected environment before that step runs.
- Restricting the whole workflow to pushes on
mainbreaks the requirement to run tests on pull requests. - Putting the condition on a step does not stop the
deployjob from targetingproduction, so a PR can still trigger environment approval flow. - Removing the environment discards required reviewers and other environment protections instead of combining them with workflow logic.
Question 8
Topic: Secure and Optimize Automation
A repository contains a Node.js app in web/. The repository root has no lockfile; only web/package-lock.json exists. The team wants faster repeated workflow runs, but the cache must be invalidated when web/package-lock.json changes.
name: web-ci
on:
push:
paths:
- 'web/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
working-directory: web
- run: npm test
working-directory: web
Which edit best satisfies this requirement?
Options:
A. Add
cache: npmandcache-dependency-path: web/package-lock.json.B. Cache
~/.npmwith key${{ github.sha }}.C. Add only
cache: npmtoactions/setup-node.D. Cache
web/node_moduleswith key${{ runner.os }}-node.
Best answer: A
Explanation: Use actions/setup-node caching with cache: npm and point cache-dependency-path to the actual lockfile in web/. That speeds repeated npm ci runs while ensuring the cache key changes when the app’s dependencies change.
For dependency caching, the key should reflect the dependency definition, not just the branch, OS, or commit. In this workflow, the dependency definition is web/package-lock.json, and the repository root does not contain a lockfile.
Using actions/setup-node with npm caching is the cleanest fit here:
cache: npmenables the npm package cache.cache-dependency-path: web/package-lock.jsontells GitHub Actions which lockfile to hash.- When that lockfile changes, the cache key changes too.
npm cistill installs from the lockfile, but repeated runs can reuse downloaded packages.
A broad node_modules cache risks stale contents, and a cache keyed by github.sha is so specific that it provides little reuse across commits.
- Adding only
cache: npmis incomplete for this monorepo because the relevant lockfile is not in the repository root. - Caching
web/node_moduleswith only the OS in the key can restore outdated dependencies after lockfile changes. - Caching
~/.npmbygithub.shacreates a nearly new cache every commit, so it does not meaningfully reduce repeated install time.
Question 9
Topic: Secure and Optimize Automation
An organization allows a third-party scanning action, but requires that the action’s code must not change between workflow runs unless maintainers review a pull request that updates the workflow file.
name: ci
on: push
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: vendor-labs/license-scan@main
- run: npm test
The team must keep using vendor-labs/license-scan. Which edit best satisfies the policy and minimizes the risk of unreviewed version changes?
Options:
A. Use
vendor-labs/license-scan@v2.4.1B. Use
vendor-labs/license-scan@mainwith read-onlypermissionsC. Use
vendor-labs/license-scan@v2D. Use
vendor-labs/license-scan@4f2c9a1d7b6e8c3f5a0d2e1b9c7f6a4d3e2b1c0f
Best answer: D
Explanation: Pinning the third-party action to a full commit SHA is the only option that fixes the workflow to one exact revision. Branch refs like @main and tag refs like @v2 can change without the workflow file changing, so they do not meet a review-before-update policy.
In GitHub Actions, the uses: value can point to a branch, a tag, or a commit SHA. Branches such as @main and tags such as @v2 are floating references, so the action publisher can move them to different commits without any change in your workflow file. Even a specific version tag is still a tag reference controlled by the publisher.
If the goal is to prevent unreviewed third-party code changes, pin the action to a full commit SHA. That makes the workflow run the same reviewed action revision every time until someone intentionally updates the SHA in a pull request.
Read-only token permissions reduce what the action can do, but they do not stop the referenced action code from changing.
- A major tag like
@v2is still a floating reference and can advance to newer action code without a workflow edit. - A specific tag like
@v2.4.1is narrower, but it is still a tag reference rather than an immutable commit identifier. - Read-only
permissionslimitGITHUB_TOKENscope, but they do not pin the third-party action source to one reviewed revision.
Question 10
Topic: Secure and Optimize Automation
A team uses a reusable workflow to build dist/app.tgz and create an artifact attestation. All third-party actions inside package.yml are already pinned to full commit SHAs, and the job uses GitHub-hosted runners. The team wants consumers to rely on the attestation as evidence of a reviewed, immutable build process.
name: release
on:
push:
tags: ['v*']
permissions:
contents: read
attestations: write
id-token: write
jobs:
package:
uses: octo-org/build-templates/.github/workflows/package.yml@main
Which edit best addresses the remaining trust limitation?
Options:
A. Pin the reusable workflow reference to a major version tag
B. Grant
contents: writeto the workflowC. Pin the reusable workflow reference to a full commit SHA
D. Add
workflow_dispatchas an additional trigger
Best answer: C
Explanation: Artifact attestations prove provenance for the run that produced an artifact, but they do not make a floating workflow reference trustworthy. Because the reusable workflow is called with @main, the build definition can change over time. Pinning it to a full commit SHA makes the attested process immutable and reviewable.
The core issue is workflow trust. An artifact attestation can accurately state which workflow run produced an artifact, but it does not guarantee that the workflow definition itself was fixed or trusted. In this snippet, the reusable workflow is referenced by @main, which is a moving branch reference. That means future runs can produce valid attestations while using different workflow logic.
- Use
attestations: writeandid-token: writeto generate the attestation. - Pin reusable workflows to a full commit SHA when provenance trust depends on immutable workflow content.
- Treat attestations as evidence of what ran, not proof that a drifting workflow reference was safe.
The key takeaway is that provenance is only as trustworthy as the workflow, dependencies, and runner environment that produced it.
- Major tag drift is weaker than a full SHA because tags such as
v1can still be moved to different commits. - More repository write access changes token scope, not whether the attested workflow definition is immutable.
- Another trigger changes how the workflow can start, but it does not make the called workflow reference trustworthy.
Continue in the web app
Use IT Mastery for interactive GitHub Actions GH-200 practice with mixed sets, timed mocks, topic drills, explanations, and progress tracking.
Try GitHub Actions GH-200 on Web