gitlab-compliance 2.4.0__tar.gz → 2.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/PKG-INFO +2 -2
  2. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/pyproject.toml +4 -2
  3. gitlab_compliance-2.6.0/src/__init__.py +1 -0
  4. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/behave_support/steps/script_steps.py +113 -18
  5. gitlab_compliance-2.6.0/src/compliance/builtin_policies/baseline-jobs.feature +45 -0
  6. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/component-inputs.feature +18 -1
  7. gitlab_compliance-2.6.0/src/compliance/builtin_policies/include-pinning.feature +46 -0
  8. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-ci-conventions.feature +22 -4
  9. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-command-substitution.feature +15 -3
  10. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-conditionals.feature +15 -3
  11. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-error-handling.feature +21 -4
  12. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-file-operations.feature +20 -4
  13. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-pinning.feature +77 -11
  14. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-pipelines.feature +15 -3
  15. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-portability.feature +23 -4
  16. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-quoting.feature +26 -5
  17. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-references.feature +12 -2
  18. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/shell/shell-security.feature +39 -6
  19. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/supply-chain/image-pinning.feature +36 -5
  20. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/supply-chain/include-versions.feature +58 -7
  21. gitlab_compliance-2.6.0/src/compliance/builtin_policies/supply-chain/service-pinning.feature +30 -0
  22. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/variable-allowlist.feature +13 -0
  23. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/console.py +25 -5
  24. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/metadata.py +12 -0
  25. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/models.py +2 -0
  26. gitlab_compliance-2.6.0/src/compliance/policy_doc.py +341 -0
  27. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/policy_fix.py +12 -12
  28. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/render.py +106 -26
  29. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/runner.py +814 -623
  30. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/script_analysis.py +384 -49
  31. gitlab_compliance-2.6.0/src/compliance/shell_lex.py +777 -0
  32. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/shell_render.py +76 -23
  33. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/gitlab_compliance.py +39 -5
  34. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/gitstrings.py +7 -4
  35. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/swagger_markdown.py +3 -1
  36. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/table_render.py +2 -1
  37. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/yaml_paths.py +2 -1
  38. gitlab_compliance-2.4.0/src/__init__.py +0 -1
  39. gitlab_compliance-2.4.0/src/compliance/builtin_policies/baseline-jobs.feature +0 -17
  40. gitlab_compliance-2.4.0/src/compliance/builtin_policies/include-pinning.feature +0 -15
  41. gitlab_compliance-2.4.0/src/compliance/builtin_policies/supply-chain/service-pinning.feature +0 -7
  42. gitlab_compliance-2.4.0/src/compliance/policy_doc.py +0 -183
  43. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/LICENSE +0 -0
  44. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/docs/index.md +0 -0
  45. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/__init__.py +0 -0
  46. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/api_config.py +0 -0
  47. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/api_enrichment.py +0 -0
  48. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/behave_support/environment.py +0 -0
  49. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/behave_support/steps/__init__.py +0 -0
  50. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/behave_support/steps/given_steps.py +0 -0
  51. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/behave_support/steps/then_steps.py +0 -0
  52. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/behave_support/steps/when_steps.py +0 -0
  53. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/builtin_policies/__init__.py +0 -0
  54. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/container_fix.py +0 -0
  55. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/gitlab_api.py +0 -0
  56. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/image_versions.py +0 -0
  57. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/include_fetch.py +0 -0
  58. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/include_fix.py +0 -0
  59. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/include_resolution.py +0 -0
  60. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/include_versions.py +0 -0
  61. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/merge_requests.py +0 -0
  62. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/model.py +0 -0
  63. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/oci_registry.py +0 -0
  64. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/release_cache.py +0 -0
  65. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/secret_redact.py +0 -0
  66. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/stash.py +0 -0
  67. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/compliance/supply_chain_fix.py +0 -0
  68. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/gitlab_docs.py +0 -0
  69. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/__init__.py +0 -0
  70. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/command_reference.py +0 -0
  71. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/common.py +0 -0
  72. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/constants.py +0 -0
  73. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/doc_controller.py +0 -0
  74. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/gitlab_reference.py +0 -0
  75. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/job_composition.py +0 -0
  76. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/logging.py +0 -0
  77. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/output_filters.py +0 -0
  78. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/pipeline_data.py +0 -0
  79. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/release.py +0 -0
  80. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/swagger_html.py +0 -0
  81. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/yaml_lines.py +0 -0
  82. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/modules/yaml_md_table.py +0 -0
  83. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/__init__.py +0 -0
  84. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/extract_job_attribute.py +0 -0
  85. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/includes.py +0 -0
  86. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/inputs.py +0 -0
  87. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/jobs.py +0 -0
  88. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/variables.py +0 -0
  89. {gitlab_compliance-2.4.0 → gitlab_compliance-2.6.0}/src/properties/workflows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitlab-compliance
3
- Version: 2.4.0
3
+ Version: 2.6.0
4
4
  Summary: BDD compliance testing for GitLab CI/CD pipelines and project settings — by MaturityBuilder
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -34,7 +34,7 @@ Requires-Dist: requests (==2.34.2)
34
34
  Requires-Dist: rich (==15.0.0)
35
35
  Requires-Dist: semver (==3.0.4)
36
36
  Requires-Dist: typer (==0.27.0)
37
- Requires-Dist: urllib3 (==2.7.0)
37
+ Requires-Dist: urllib3 (==2.8.0)
38
38
  Project-URL: Documentation, https://maturitybuilder.github.io/gitlab-compliance/
39
39
  Project-URL: Home Page, https://maturitybuilder.github.io/gitlab-compliance/
40
40
  Project-URL: Repository, https://github.com/MaturityBuilder/gitlab-compliance
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gitlab-compliance"
3
- version = "2.4.0"
3
+ version = "2.6.0"
4
4
  dynamic = [ "readme" ]
5
5
  license = "Apache-2.0"
6
6
  requires-python = ">=3.12"
@@ -42,7 +42,7 @@ pytablewriter = "1.2.1"
42
42
  oyaml = "*"
43
43
  typer = "0.27.0"
44
44
  markdown-analysis = "0.1.5"
45
- urllib3 = "2.7.0"
45
+ urllib3 = "2.8.0"
46
46
  jq = "1.12.0"
47
47
  click = "^8.1.8"
48
48
  semver = "3.0.4"
@@ -62,6 +62,8 @@ gitlab-compliance = 'src.gitlab_compliance:gitlab_compliance'
62
62
  [tool.poetry.group.dev.dependencies]
63
63
  setuptools = "83.0.0"
64
64
  pytest = "^9.0.3"
65
+ pytest-xdist = "^3.8.0"
66
+ pytest-cov = "^6.2.1"
65
67
  pre-commit = "^4.6.1"
66
68
  pyyaml = "^6.0.3"
67
69
  coverage = "^7.9.2"
@@ -0,0 +1 @@
1
+ __version__ = "2.4.0" # x-release-please-version
@@ -6,6 +6,33 @@ from behave import given, then, when
6
6
 
7
7
  from src.compliance.behave_support.environment import _skip_remaining_steps
8
8
  from src.compliance.script_analysis import (
9
+ evidence_backticks,
10
+ evidence_bashism_in_posix_test,
11
+ evidence_bashisms_without_bash,
12
+ evidence_chmod_777,
13
+ evidence_curl_missing_fail,
14
+ evidence_dangerous_rm,
15
+ evidence_deprecated_ci_build,
16
+ evidence_download_without_checksum,
17
+ evidence_eval,
18
+ evidence_hardcoded_secrets,
19
+ evidence_insecure_temp_files,
20
+ evidence_invalid_shebang,
21
+ evidence_masked_failure,
22
+ evidence_missing_strict_mode,
23
+ evidence_nested_backticks,
24
+ evidence_pipeline_without_pipefail,
25
+ evidence_posix_bashisms,
26
+ evidence_remote_pipe,
27
+ evidence_unpinned_docker,
28
+ evidence_unpinned_manager,
29
+ evidence_unquoted_command_substitution,
30
+ evidence_unquoted_path_variables,
31
+ evidence_unquoted_test_variables,
32
+ evidence_unquoted_variables,
33
+ evidence_unresolved_references,
34
+ evidence_unsafe_array_expansion,
35
+ evidence_unverified_git_clone,
9
36
  format_script_violation,
10
37
  job_has_effective_script,
11
38
  job_has_script_field,
@@ -49,6 +76,19 @@ from src.compliance.script_analysis import (
49
76
  from src.compliance.stash import filter_entities, name_starts_with
50
77
 
51
78
 
79
+ def _shell_check_verbose(context) -> bool:
80
+ config = getattr(context, "config", None)
81
+ userdata = getattr(config, "userdata", None) if config is not None else None
82
+ if not isinstance(userdata, dict):
83
+ return False
84
+ return str(userdata.get("shell_check_verbose", "false")).lower() in {
85
+ "true",
86
+ "1",
87
+ "yes",
88
+ "on",
89
+ }
90
+
91
+
52
92
  def _set_stash(context, entities: list[dict]):
53
93
  context.stash = list(entities)
54
94
  context.step_mode = "given"
@@ -72,32 +112,36 @@ def _apply_filter(context, entities: list[dict], reason: str):
72
112
  context.stash = entities
73
113
 
74
114
 
75
- def _assert_none(context, predicate, message: str):
115
+ def _assert_none(context, predicate, message: str, evidence=None):
76
116
  if context.scenario_skipped:
77
117
  return
78
118
  context.step_mode = "then"
79
119
  if not context.stash:
80
120
  raise AssertionError("No entities in stash to assert against.")
81
- failures = [
82
- format_script_violation(entity, message)
83
- for entity in context.stash
84
- if predicate(entity)
85
- ]
121
+ verbose = _shell_check_verbose(context)
122
+ failures = []
123
+ for entity in context.stash:
124
+ if not predicate(entity):
125
+ continue
126
+ found = evidence(entity) if verbose and evidence is not None else None
127
+ failures.append(format_script_violation(entity, message, found=found))
86
128
  if failures:
87
129
  raise AssertionError("; ".join(failures))
88
130
 
89
131
 
90
- def _assert_all_true(context, predicate, message: str):
132
+ def _assert_all_true(context, predicate, message: str, evidence=None):
91
133
  if context.scenario_skipped:
92
134
  return
93
135
  context.step_mode = "then"
94
136
  if not context.stash:
95
137
  raise AssertionError("No entities in stash to assert against.")
96
- failures = [
97
- format_script_violation(entity, message)
98
- for entity in context.stash
99
- if not predicate(entity)
100
- ]
138
+ verbose = _shell_check_verbose(context)
139
+ failures = []
140
+ for entity in context.stash:
141
+ if predicate(entity):
142
+ continue
143
+ found = evidence(entity) if verbose and evidence is not None else None
144
+ failures.append(format_script_violation(entity, message, found=found))
101
145
  if failures:
102
146
  raise AssertionError("; ".join(failures))
103
147
 
@@ -230,6 +274,7 @@ def then_variables_quoted(context):
230
274
  context,
231
275
  script_has_unquoted_variables,
232
276
  "Unquoted variable expansions found",
277
+ evidence=evidence_unquoted_variables,
233
278
  )
234
279
 
235
280
 
@@ -239,6 +284,7 @@ def then_path_variables_quoted(context):
239
284
  context,
240
285
  script_has_unquoted_path_variables,
241
286
  "Unquoted variables used in file paths",
287
+ evidence=evidence_unquoted_path_variables,
242
288
  )
243
289
 
244
290
 
@@ -248,6 +294,7 @@ def then_cmd_sub_quoted(context):
248
294
  context,
249
295
  script_has_unquoted_command_substitution,
250
296
  "Unquoted command substitution found",
297
+ evidence=evidence_unquoted_command_substitution,
251
298
  )
252
299
 
253
300
 
@@ -257,6 +304,7 @@ def then_array_at_syntax(context):
257
304
  context,
258
305
  script_has_unsafe_array_expansion,
259
306
  "Unsafe array expansion found",
307
+ evidence=evidence_unsafe_array_expansion,
260
308
  )
261
309
 
262
310
 
@@ -266,6 +314,7 @@ def then_strict_mode_table(context):
266
314
  context,
267
315
  script_enables_strict_mode,
268
316
  "Multi-line script missing set -e/-u/pipefail",
317
+ evidence=evidence_missing_strict_mode,
269
318
  )
270
319
 
271
320
 
@@ -277,7 +326,12 @@ def then_failure_handled(context):
277
326
  return True
278
327
  return not script_masks_failures(entity)
279
328
 
280
- _assert_all_true(context, ok, "Command failures are not handled explicitly")
329
+ _assert_all_true(
330
+ context,
331
+ ok,
332
+ "Command failures are not handled explicitly",
333
+ evidence=evidence_masked_failure,
334
+ )
281
335
 
282
336
 
283
337
  @then("functions must propagate failures")
@@ -291,6 +345,7 @@ def then_functions_propagate(context):
291
345
  context,
292
346
  script_functions_mask_failures,
293
347
  "Functions mask failures with || true",
348
+ evidence=evidence_masked_failure,
294
349
  )
295
350
 
296
351
 
@@ -300,6 +355,7 @@ def then_file_paths_quoted(context):
300
355
  context,
301
356
  script_has_unquoted_path_variables,
302
357
  "Unquoted file paths found",
358
+ evidence=evidence_unquoted_path_variables,
303
359
  )
304
360
 
305
361
 
@@ -309,6 +365,7 @@ def then_mktemp_used(context):
309
365
  context,
310
366
  script_uses_insecure_temp_files,
311
367
  "Insecure temporary file creation without mktemp",
368
+ evidence=evidence_insecure_temp_files,
312
369
  )
313
370
 
314
371
 
@@ -318,12 +375,18 @@ def then_rm_protected(context):
318
375
  context,
319
376
  script_has_dangerous_rm,
320
377
  "Dangerous recursive rm without path validation",
378
+ evidence=evidence_dangerous_rm,
321
379
  )
322
380
 
323
381
 
324
382
  @then("command substitution must use dollar parentheses")
325
383
  def then_no_backticks(context):
326
- _assert_none(context, script_uses_backticks, "Legacy backtick substitution found")
384
+ _assert_none(
385
+ context,
386
+ script_uses_backticks,
387
+ "Legacy backtick substitution found",
388
+ evidence=evidence_backticks,
389
+ )
327
390
 
328
391
 
329
392
  @then("nested command substitution must use dollar parentheses")
@@ -332,6 +395,7 @@ def then_nested_dollar(context):
332
395
  context,
333
396
  script_has_nested_backticks,
334
397
  "Nested backtick substitution found",
398
+ evidence=evidence_nested_backticks,
335
399
  )
336
400
 
337
401
 
@@ -341,6 +405,7 @@ def then_posix_operators(context):
341
405
  context,
342
406
  script_has_bashism_in_posix_test,
343
407
  "Non-portable operators in [ tests",
408
+ evidence=evidence_bashism_in_posix_test,
344
409
  )
345
410
 
346
411
 
@@ -350,6 +415,7 @@ def then_test_vars_quoted(context):
350
415
  context,
351
416
  script_has_unquoted_test_variables,
352
417
  "Unquoted variables in test expressions",
418
+ evidence=evidence_unquoted_test_variables,
353
419
  )
354
420
 
355
421
 
@@ -359,12 +425,15 @@ def then_pipefail(context):
359
425
  context,
360
426
  lambda e: (not script_has_pipeline(e)) or script_has_pipefail(e),
361
427
  "Pipeline used without pipefail",
428
+ evidence=evidence_pipeline_without_pipefail,
362
429
  )
363
430
 
364
431
 
365
432
  @then("eval must not be used")
366
433
  def then_no_eval(context):
367
- _assert_none(context, script_uses_eval, "eval is used in script")
434
+ _assert_none(
435
+ context, script_uses_eval, "eval is used in script", evidence=evidence_eval
436
+ )
368
437
 
369
438
 
370
439
  @then("untrusted remote scripts must not be executed")
@@ -373,6 +442,7 @@ def then_no_remote_pipe(context):
373
442
  context,
374
443
  script_has_remote_pipe_to_shell,
375
444
  "Remote pipe to shell (curl|sh) detected",
445
+ evidence=evidence_remote_pipe,
376
446
  )
377
447
 
378
448
 
@@ -382,6 +452,7 @@ def then_user_input_safe(context):
382
452
  context,
383
453
  script_has_unquoted_variables,
384
454
  "User/CI variables used without quoting",
455
+ evidence=evidence_unquoted_variables,
385
456
  )
386
457
 
387
458
 
@@ -391,6 +462,7 @@ def then_no_secrets(context):
391
462
  context,
392
463
  script_has_hardcoded_secrets,
393
464
  "Hardcoded secret pattern detected",
465
+ evidence=evidence_hardcoded_secrets,
394
466
  )
395
467
 
396
468
 
@@ -400,6 +472,7 @@ def then_downloads_checksum(context):
400
472
  context,
401
473
  script_downloads_without_checksum,
402
474
  "Download without nearby checksum verification",
475
+ evidence=evidence_download_without_checksum,
403
476
  )
404
477
 
405
478
 
@@ -416,7 +489,12 @@ def then_packages_pinned(context, manager):
416
489
  predicate = predicates.get(manager)
417
490
  if predicate is None:
418
491
  raise AssertionError(f"Unknown package manager '{manager}'")
419
- _assert_none(context, predicate, f"Unpinned {manager} package install")
492
+ _assert_none(
493
+ context,
494
+ predicate,
495
+ f"Unpinned {manager} package install",
496
+ evidence=lambda entity: evidence_unpinned_manager(entity, manager),
497
+ )
420
498
 
421
499
 
422
500
  @then("docker commands must pin container images to a tag or sha256 digest")
@@ -425,6 +503,7 @@ def then_docker_images_pinned(context):
425
503
  context,
426
504
  script_has_unpinned_docker_image,
427
505
  "Docker command references an unpinned container image",
506
+ evidence=evidence_unpinned_docker,
428
507
  )
429
508
 
430
509
 
@@ -434,12 +513,18 @@ def then_git_clone_verified(context):
434
513
  context,
435
514
  script_has_unverified_git_clone,
436
515
  "git clone without commit/tag verification",
516
+ evidence=evidence_unverified_git_clone,
437
517
  )
438
518
 
439
519
 
440
520
  @then("curl must use fail flag")
441
521
  def then_curl_fail(context):
442
- _assert_none(context, script_curl_missing_fail, "curl without --fail/-f")
522
+ _assert_none(
523
+ context,
524
+ script_curl_missing_fail,
525
+ "curl without --fail/-f",
526
+ evidence=evidence_curl_missing_fail,
527
+ )
443
528
 
444
529
 
445
530
  @then("deprecated CI_BUILD variables must not be used")
@@ -450,6 +535,7 @@ def then_no_ci_build(context):
450
535
  context,
451
536
  script_uses_deprecated_ci_build_vars,
452
537
  "Deprecated $CI_BUILD_* variable used",
538
+ evidence=evidence_deprecated_ci_build,
453
539
  )
454
540
 
455
541
 
@@ -459,6 +545,7 @@ def then_no_unresolved_refs(context):
459
545
  context,
460
546
  script_has_unresolved_references,
461
547
  "Unresolved !reference in script blocks",
548
+ evidence=evidence_unresolved_references,
462
549
  )
463
550
 
464
551
 
@@ -468,6 +555,7 @@ def then_shebang_valid(context):
468
555
  context,
469
556
  lambda e: (not script_has_shebang(e)) or script_shebang_is_valid(e),
470
557
  "Invalid shebang in script block",
558
+ evidence=evidence_invalid_shebang,
471
559
  )
472
560
 
473
561
 
@@ -477,6 +565,7 @@ def then_bash_declared(context):
477
565
  context,
478
566
  script_bashisms_without_bash_shebang,
479
567
  "Bash-specific syntax without bash shebang",
568
+ evidence=evidence_bashisms_without_bash,
480
569
  )
481
570
 
482
571
 
@@ -486,9 +575,15 @@ def then_posix_no_bashisms(context):
486
575
  context,
487
576
  script_posix_shebang_with_bashisms,
488
577
  "Bashisms used with sh shebang",
578
+ evidence=evidence_posix_bashisms,
489
579
  )
490
580
 
491
581
 
492
582
  @then("chmod 777 must not be used")
493
583
  def then_no_chmod_777(context):
494
- _assert_none(context, script_has_chmod_777, "chmod 777 is used")
584
+ _assert_none(
585
+ context,
586
+ script_has_chmod_777,
587
+ "chmod 777 is used",
588
+ evidence=evidence_chmod_777,
589
+ )
@@ -0,0 +1,45 @@
1
+ # METADATA
2
+ # title: Baseline job controls
3
+ # description: Core job image and execution rules bundled with gitlab-compliance.
4
+ # custom:
5
+ # id: GLCI-BUILTIN-BASELINE
6
+ # severity: HIGH
7
+ # owasp_cicd:
8
+ # - CICD-SEC-3
9
+ # - CICD-SEC-7
10
+ # iso27001:
11
+ # - A.8.25
12
+ # - A.8.9
13
+ Feature: Baseline job controls
14
+
15
+ # METADATA
16
+ # title: Job images must not use the latest tag
17
+ # custom:
18
+ # id: GLCI-BUILTIN-BASELINE-01
19
+ # severity: HIGH
20
+ # owasp_cicd:
21
+ # - CICD-SEC-3
22
+ # - CICD-SEC-9
23
+ # iso27001:
24
+ # - A.8.25
25
+ # - A.8.9
26
+ Scenario: Job images must not use the latest tag
27
+ Given I have any job defined
28
+ When it has image
29
+ Then its image must not match ":latest$"
30
+
31
+ # METADATA
32
+ # title: Non-template jobs must define rules
33
+ # custom:
34
+ # id: GLCI-BUILTIN-BASELINE-02
35
+ # severity: HIGH
36
+ # owasp_cicd:
37
+ # - CICD-SEC-1
38
+ # - CICD-SEC-7
39
+ # iso27001:
40
+ # - A.8.9
41
+ # - A.8.32
42
+ Scenario: Non-template jobs must define rules
43
+ Given I have any job defined
44
+ When its name does not start with "."
45
+ Then it must contain rules
@@ -2,10 +2,27 @@
2
2
  # title: Component input constraints
3
3
  # description: Advanced Scenario Outline for component inputs and conditional fields.
4
4
  # custom:
5
- # id: GLCI-BUILTIN-COMPONENT-INPUTS
5
+ # id: GLCI-BUILTIN-COMPONENT
6
6
  # severity: MEDIUM
7
+ # owasp_cicd:
8
+ # - CICD-SEC-7
9
+ # - CICD-SEC-8
10
+ # iso27001:
11
+ # - A.8.9
12
+ # - A.8.25
7
13
  Feature: Component input constraints
8
14
 
15
+ # METADATA
16
+ # title: Component input mode constrains dependent fields
17
+ # custom:
18
+ # id: GLCI-BUILTIN-COMPONENT-01
19
+ # severity: MEDIUM
20
+ # owasp_cicd:
21
+ # - CICD-SEC-7
22
+ # - CICD-SEC-8
23
+ # iso27001:
24
+ # - A.8.9
25
+ # - A.8.25
9
26
  Scenario Outline: Component input mode constrains dependent fields
10
27
  Given I have include type "component" defined
11
28
  When the entity input <input> equals <value>
@@ -0,0 +1,46 @@
1
+ # METADATA
2
+ # title: Include pinning essentials
3
+ # description: Baseline checks for component and fragment version pinning.
4
+ # custom:
5
+ # id: GLCI-BUILTIN-INCPIN
6
+ # severity: HIGH
7
+ # owasp_cicd:
8
+ # - CICD-SEC-3
9
+ # - CICD-SEC-9
10
+ # iso27001:
11
+ # - A.8.25
12
+ # - A.8.9
13
+ # - A.5.19
14
+ Feature: Include pinning essentials
15
+
16
+ # METADATA
17
+ # title: Component includes must not track a branch
18
+ # custom:
19
+ # id: GLCI-BUILTIN-INCPIN-01
20
+ # severity: HIGH
21
+ # owasp_cicd:
22
+ # - CICD-SEC-3
23
+ # - CICD-SEC-9
24
+ # iso27001:
25
+ # - A.8.25
26
+ # - A.8.9
27
+ # - A.5.19
28
+ Scenario: Component includes must not track a branch
29
+ Given I have include type "component" defined
30
+ Then its version must not match "^(main|master|develop)$"
31
+
32
+ # METADATA
33
+ # title: Component includes must use semver
34
+ # custom:
35
+ # id: GLCI-BUILTIN-INCPIN-02
36
+ # severity: HIGH
37
+ # owasp_cicd:
38
+ # - CICD-SEC-3
39
+ # - CICD-SEC-9
40
+ # iso27001:
41
+ # - A.8.25
42
+ # - A.8.9
43
+ # - A.5.19
44
+ Scenario: Component includes must use semver
45
+ Given I have include type "component" defined
46
+ Then its version must match "^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?$"
@@ -2,15 +2,24 @@
2
2
  # title: GitLab CI script conventions
3
3
  # description: CI-specific hygiene for curl and deprecated variables.
4
4
  # custom:
5
- # id: GLCI-SHELL-CI
5
+ # id: GLCI-BUILTIN-SHELL-CI
6
6
  # severity: MEDIUM
7
+ # owasp_cicd:
8
+ # - CICD-SEC-7
9
+ # iso27001:
10
+ # - A.8.25
11
+ # - A.8.28
7
12
  Feature: GitLab CI script conventions
8
13
 
9
14
  # METADATA
10
15
  # title: curl uses fail flag
11
16
  # custom:
12
- # id: GLCI-SHELL-CI-001
17
+ # id: GLCI-BUILTIN-SHELL-CI-01
13
18
  # severity: MEDIUM
19
+ # owasp_cicd:
20
+ # - CICD-SEC-7
21
+ # iso27001:
22
+ # - A.8.28
14
23
  Scenario: curl must use fail flag
15
24
  Given I have any job with effective script defined
16
25
  Then curl must use fail flag
@@ -18,8 +27,13 @@ Feature: GitLab CI script conventions
18
27
  # METADATA
19
28
  # title: Deprecated CI_BUILD variables are not used
20
29
  # custom:
21
- # id: GLCI-SHELL-CI-002
30
+ # id: GLCI-BUILTIN-SHELL-CI-02
22
31
  # severity: MEDIUM
32
+ # owasp_cicd:
33
+ # - CICD-SEC-7
34
+ # iso27001:
35
+ # - A.8.9
36
+ # - A.8.25
23
37
  Scenario: Deprecated CI_BUILD variables must not be used
24
38
  Given I have any job with effective script defined
25
39
  Then deprecated CI_BUILD variables must not be used
@@ -27,8 +41,12 @@ Feature: GitLab CI script conventions
27
41
  # METADATA
28
42
  # title: CI variables should be quoted
29
43
  # custom:
30
- # id: GLCI-SHELL-CI-003
44
+ # id: GLCI-BUILTIN-SHELL-CI-03
31
45
  # severity: MEDIUM
46
+ # owasp_cicd:
47
+ # - CICD-SEC-7
48
+ # iso27001:
49
+ # - A.8.28
32
50
  Scenario: CI variables should be quoted
33
51
  Given I have any job with effective script defined
34
52
  Then user supplied variables must be quoted or validated
@@ -2,15 +2,23 @@
2
2
  # title: Command substitution standards
3
3
  # description: Prefer modern dollar-parentheses substitution over backticks.
4
4
  # custom:
5
- # id: GLCI-SHELL-SUB
5
+ # id: GLCI-BUILTIN-SHELL-SUB
6
6
  # severity: MEDIUM
7
+ # owasp_cicd:
8
+ # - CICD-SEC-7
9
+ # iso27001:
10
+ # - A.8.28
7
11
  Feature: Command substitution
8
12
 
9
13
  # METADATA
10
14
  # title: Legacy backticks are not used
11
15
  # custom:
12
- # id: GLCI-SHELL-SUB-001
16
+ # id: GLCI-BUILTIN-SHELL-SUB-01
13
17
  # severity: MEDIUM
18
+ # owasp_cicd:
19
+ # - CICD-SEC-7
20
+ # iso27001:
21
+ # - A.8.28
14
22
  Scenario: Legacy backticks are not used
15
23
  Given I have any job with effective script defined
16
24
  Then command substitution must use dollar parentheses
@@ -18,8 +26,12 @@ Feature: Command substitution
18
26
  # METADATA
19
27
  # title: Nested command substitution is readable
20
28
  # custom:
21
- # id: GLCI-SHELL-SUB-002
29
+ # id: GLCI-BUILTIN-SHELL-SUB-02
22
30
  # severity: MEDIUM
31
+ # owasp_cicd:
32
+ # - CICD-SEC-7
33
+ # iso27001:
34
+ # - A.8.28
23
35
  Scenario: Nested command substitution is readable
24
36
  Given I have any job with effective script defined
25
37
  Then nested command substitution must use dollar parentheses
@@ -2,15 +2,23 @@
2
2
  # title: Conditional expression standards
3
3
  # description: Portable and safely quoted test expressions.
4
4
  # custom:
5
- # id: GLCI-SHELL-TEST
5
+ # id: GLCI-BUILTIN-SHELL-TEST
6
6
  # severity: MEDIUM
7
+ # owasp_cicd:
8
+ # - CICD-SEC-7
9
+ # iso27001:
10
+ # - A.8.28
7
11
  Feature: Conditional expressions
8
12
 
9
13
  # METADATA
10
14
  # title: Test operators are portable
11
15
  # custom:
12
- # id: GLCI-SHELL-TEST-001
16
+ # id: GLCI-BUILTIN-SHELL-TEST-01
13
17
  # severity: MEDIUM
18
+ # owasp_cicd:
19
+ # - CICD-SEC-7
20
+ # iso27001:
21
+ # - A.8.28
14
22
  Scenario: Test operators are portable
15
23
  Given I have any job with effective script defined
16
24
  Then POSIX compliant operators must be used
@@ -18,8 +26,12 @@ Feature: Conditional expressions
18
26
  # METADATA
19
27
  # title: Variables are quoted in test statements
20
28
  # custom:
21
- # id: GLCI-SHELL-TEST-002
29
+ # id: GLCI-BUILTIN-SHELL-TEST-02
22
30
  # severity: HIGH
31
+ # owasp_cicd:
32
+ # - CICD-SEC-7
33
+ # iso27001:
34
+ # - A.8.28
23
35
  Scenario: Variables are quoted in test statements
24
36
  Given I have any job with effective script defined
25
37
  Then variables in test expressions must be quoted