Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-gitlabcis for
openSUSE:Factory checked in at 2026-03-18 16:50:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-gitlabcis (Old)
and /work/SRC/openSUSE:Factory/.python-gitlabcis.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-gitlabcis"
Wed Mar 18 16:50:59 2026 rev:10 rq:1340835 version:1.19.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-gitlabcis/python-gitlabcis.changes
2026-03-17 19:06:21.041279547 +0100
+++
/work/SRC/openSUSE:Factory/.python-gitlabcis.new.8177/python-gitlabcis.changes
2026-03-18 16:52:45.122633358 +0100
@@ -1,0 +2,7 @@
+Wed Mar 18 06:50:59 UTC 2026 - Johannes Kastl
<[email protected]>
+
+- update to 1.19.0:
+ * Feature
+ - feat: Add protected branches at the group level (#125) (cbfb1f0)
+
+-------------------------------------------------------------------
Old:
----
gitlabcis-1.18.0.tar.gz
New:
----
gitlabcis-1.19.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-gitlabcis.spec ++++++
--- /var/tmp/diff_new_pack.ioGN84/_old 2026-03-18 16:52:45.642655074 +0100
+++ /var/tmp/diff_new_pack.ioGN84/_new 2026-03-18 16:52:45.642655074 +0100
@@ -17,7 +17,7 @@
Name: python-gitlabcis
-Version: 1.18.0
+Version: 1.19.0
Release: 0
Summary: An automated tool that assesses the GitLab CIS benchmarks
against a project
License: MIT
++++++ gitlabcis-1.18.0.tar.gz -> gitlabcis-1.19.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/gitlabcis-1.18.0/.gitlab/merge_request_templates/default.md
new/gitlabcis-1.19.0/.gitlab/merge_request_templates/default.md
--- old/gitlabcis-1.18.0/.gitlab/merge_request_templates/default.md
2026-03-16 23:29:53.000000000 +0100
+++ new/gitlabcis-1.19.0/.gitlab/merge_request_templates/default.md
2026-03-18 03:18:30.000000000 +0100
@@ -87,7 +87,7 @@
6. Validate the change against an input:
```sh
- gitlabcis https://gitlab.example.com
+ venv/bin/gitlabcis https://gitlab.example.com
```
See the [docs](../../docs/readme.md) for more details on usage.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gitlabcis-1.18.0/CHANGELOG.md
new/gitlabcis-1.19.0/CHANGELOG.md
--- old/gitlabcis-1.18.0/CHANGELOG.md 2026-03-16 23:30:10.000000000 +0100
+++ new/gitlabcis-1.19.0/CHANGELOG.md 2026-03-18 03:18:47.000000000 +0100
@@ -1,5 +1,11 @@
# CHANGELOG
+## v1.19.0 (2026-03-18)
+
+### Feature
+
+* feat: Add protected branches at the group level (#125)
([`cbfb1f0`](https://gitlab.com/gitlab-security-oss/cis/gitlabcis/-/commit/cbfb1f06f75a2dd59788d07d3cb31e157be61435))
+
## v1.18.0 (2026-03-16)
### Feature
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gitlabcis-1.18.0/CONTRIBUTING.md
new/gitlabcis-1.19.0/CONTRIBUTING.md
--- old/gitlabcis-1.18.0/CONTRIBUTING.md 2026-03-16 23:29:53.000000000
+0100
+++ new/gitlabcis-1.19.0/CONTRIBUTING.md 2026-03-18 03:18:30.000000000
+0100
@@ -69,6 +69,9 @@
# gitlabcis should now be added to the PATH:
gitlabcis https://gitlab.example.com/path/to/project
+# (if under venv):
+venv/bin/gitlabcis https://gitlab.example.com/path/to/project
+
# for CLI arg help see:
gitlabcis --help
```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gitlabcis-1.18.0/PKG-INFO
new/gitlabcis-1.19.0/PKG-INFO
--- old/gitlabcis-1.18.0/PKG-INFO 2026-03-16 23:30:20.650512000 +0100
+++ new/gitlabcis-1.19.0/PKG-INFO 2026-03-18 03:18:56.378135200 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: gitlabcis
-Version: 1.18.0
+Version: 1.19.0
Summary: An automated tool that assesses the GitLab CIS benchmarks against a
project.
Author-email: Nate Rosandich <[email protected]>, Neil McDonald
<[email protected]>, Mitra JozeNazemian
<[email protected]>
License: MIT License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gitlabcis-1.18.0/docs/limitations.md
new/gitlabcis-1.19.0/docs/limitations.md
--- old/gitlabcis-1.18.0/docs/limitations.md 2026-03-16 23:29:53.000000000
+0100
+++ new/gitlabcis-1.19.0/docs/limitations.md 2026-03-18 03:18:30.000000000
+0100
@@ -17,14 +17,12 @@
| id | name | Limitation |
| ---- | ------ | ------------ |
| 1.1.4 | code_approval_dismissals | For `Group` input types, we require a
change upstream on the `python-gitlab` dependency (ref: [MR approval settings
Group Level
#3165](https://github.com/python-gitlab/python-gitlab/issues/3165)). |
-| 1.1.5 | code_dismissal_restrictions | Trusted users cannot be automatically
checked. The control will `PASS` for projects that have protected branches, and
`FAIL` if none are set. For `Group` input types, we require a change upstream
on the `python-gitlab` dependency (ref: [Protected Branches Group Level
#3164](https://github.com/python-gitlab/python-gitlab/issues/3164)). |
-| 1.1.7 | code_changes_require_code_owners | The recommendation is only set
for the `default` branch. This function does not iterate over all protected
branches. Additionally, if a user removes the protected status of their default
branch, then creates a new protected branch. Only the protected branch is
checked, skipping the default. For `Group` input types, we require a change
upstream on the `python-gitlab` dependency (ref: [Protected Branches Group
Level #3164](https://github.com/python-gitlab/python-gitlab/issues/3164)). |
+| 1.1.5 | code_dismissal_restrictions | Trusted users cannot be automatically
checked. The control will `PASS` for projects & groups that have protected
branches, and `FAIL` if none are set. |
+| 1.1.7 | code_changes_require_code_owners | The recommendation is only set
for the `default` branch. For `Project` scans, this function does not iterate
over all protected branches, but it does for `Group` scans. Additionally, if a
user removes the protected status of their default branch, then creates a new
protected branch. Only the protected branch is checked, skipping the default. |
| 1.1.11 | comments_resolved_before_merging | For `Group` input types, the
following [feature
request](https://gitlab.com/gitlab-org/gitlab/-/issues/534608) needs to be
created, then an upstream change created in `python-gitlab` in order for us to
assess this. |
| 1.1.12 | commits_must_be_signed_before_merging | This control will return a
`SKIP` if the [push rules](https://docs.gitlab.com/api/group_push_rules/)
feature is not enabled. |
| 1.1.14 | branch_protections_for_admins | Requires admin permissions to get a
`PASS`/`FAIL` - additionally, gitlab.com `FAIL`'s this, because we allow group
owners to manage default branch protections (by design). |
-| 1.1.15 | merging_restrictions | This requires to iterate over every
protected branch, which for large projects takes quite some time. We cannot
distinguish between trusted & untrusted users, as the recommendation states
these must be trusted users, this function does not `FAIL` based on this. For
`Group` input types, we require a change upstream on the `python-gitlab`
dependency (ref: [Protected Branches Group Level
#3164](https://github.com/python-gitlab/python-gitlab/issues/3164)). |
-| 1.1.16 | ensure_force_push_is_denied | For `Group` input types, we require a
change upstream on the `python-gitlab` dependency (ref: [Protected Branches
Group Level
#3164](https://github.com/python-gitlab/python-gitlab/issues/3164)). |
-| 1.1.17 | deny_branch_deletions | For `Group` input types, we require a
change upstream on the `python-gitlab` dependency (ref: [Protected Branches
Group Level
#3164](https://github.com/python-gitlab/python-gitlab/issues/3164)). |
+| 1.1.15 | merging_restrictions | This requires to iterate over every
protected branch, which for large projects takes quite some time. We cannot
distinguish between trusted & untrusted users, as the recommendation states
these must be trusted users, this function does not `FAIL` based on this. For
`Group` input types, we iterate through all protected branches to determine if
`force_push` is allowed. `FAIL` if it's allowed, `PASS` if not. |
| 1.1.19 | audit_branch_protections | Ensuring that any changes to branch
protections are audited requires reviewing logs generated on the instance.
Enabling/disabling audit_events isn't toggle-able and if the automation could
query the `protected_branch_updated` events, it still would not concretely
answer if the events were audited. |
| 1.2.1 | public_repos_have_security_file | The control will `SKIP` if the
repository is not public. If the `SECURITY.md` file does not exist in the root
directory of the default branch in the repository, it will `FAIL`. |
| 1.2.3 | limit_repo_deletions | If a project that contains 1,000+ members as
a result of nested-group permissions, this control will take a long time to
finish. As such, it will return `SKIP` until a solution is found. |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gitlabcis-1.18.0/gitlabcis/__init__.py
new/gitlabcis-1.19.0/gitlabcis/__init__.py
--- old/gitlabcis-1.18.0/gitlabcis/__init__.py 2026-03-16 23:30:10.000000000
+0100
+++ new/gitlabcis-1.19.0/gitlabcis/__init__.py 2026-03-18 03:18:47.000000000
+0100
@@ -9,4 +9,4 @@
# -------------------------------------------------------------------------
__author__ = '[email protected]'
-__version__ = '1.18.0' # noqa: E999
+__version__ = '1.19.0' # noqa: E999
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/gitlabcis-1.18.0/gitlabcis/benchmarks/source_code_1/code_changes_1_1.py
new/gitlabcis-1.19.0/gitlabcis/benchmarks/source_code_1/code_changes_1_1.py
--- old/gitlabcis-1.18.0/gitlabcis/benchmarks/source_code_1/code_changes_1_1.py
2026-03-16 23:29:53.000000000 +0100
+++ new/gitlabcis-1.19.0/gitlabcis/benchmarks/source_code_1/code_changes_1_1.py
2026-03-18 03:18:30.000000000 +0100
@@ -161,12 +161,18 @@
return {False: 'No restrictions on who can dismiss code '
'changes'}
+ elif kwargs.get('isGroup'):
+ if glEntity.protectedbranches.list(get_all=False):
+ return {
+ True: 'Protected branches found, restrictions set on '
+ 'who can dismiss code changes'}
+
+ return {False: 'No restrictions on who can dismiss code '
+ 'changes'}
+
elif kwargs.get('isInstance'):
return {None: 'Not applicable at instance level'}
- elif kwargs.get('isGroup'):
- return {None: 'Not yet implemented for groups'}
-
except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
GitlabListError) as e:
if e.response_code in [401, 403]:
@@ -258,12 +264,32 @@
except AttributeError:
return {None: 'CODEOWNERS approval not available'}
+ elif kwargs.get('isGroup'):
+ try:
+ protectedBranches = glEntity.protectedbranches.list(get_all=True)
+
+ if not protectedBranches:
+ return {False: 'No protected branches found'}
+
+ for branch in protectedBranches:
+ if branch.code_owner_approval_required is True:
+ return {True: 'CODEOWNERS approval is required'}
+
+ return {False: 'CODEOWNERS approval is not configured'}
+
+ except (GitlabHttpError, GitlabGetError,
+ GitlabAuthenticationError) as e:
+ if e.response_code == 403 and e.error_message == '403 Forbidden':
+ return {None: 'Insufficient permissions'}
+
+ if e.response_code == 404:
+ return {False: 'No protected branches found'}
+ except AttributeError:
+ return {None: 'CODEOWNERS approval not available'}
+
elif kwargs.get('isInstance'):
return {None: 'Not applicable at instance level'}
- elif kwargs.get('isGroup'):
- return {None: 'Not yet implemented for groups'}
-
# -------------------------------------------------------------------------
@@ -570,7 +596,23 @@
return {None: 'Insufficient permissions'}
elif kwargs.get('isGroup'):
- return {None: 'Not yet implemented for groups'}
+ try:
+ protectedBranches = glEntity.protectedbranches.list(get_all=True)
+
+ if len(protectedBranches) == 0:
+ return {False: 'No protected branches found'}
+
+ for branch in protectedBranches:
+
+ if branch.allow_force_push is True:
+ return {False: 'Protected branches allow force push'}
+
+ return {True: 'Protected branches do not allow force push'}
+
+ except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
+ GitlabListError) as e:
+ if e.response_code in [401, 403]:
+ return {None: 'Insufficient permissions'}
# -------------------------------------------------------------------------
@@ -621,7 +663,22 @@
return {None: 'Insufficient permissions'}
elif kwargs.get('isGroup'):
- return {None: 'Not yet implemented for groups'}
+ try:
+ protectedBranches = glEntity.protectedbranches.list(get_all=True)
+
+ if not protectedBranches:
+ return {False: 'No protected branches found'}
+
+ for branch in protectedBranches:
+ if branch.allow_force_push is True:
+ return {False: 'A protected branch allows force push'}
+
+ return {True: 'No protected branches allow force push'}
+
+ except (GitlabHttpError, GitlabGetError,
+ GitlabAuthenticationError) as e:
+ if e.response_code in [401, 403, 404]:
+ return {None: 'Insufficient permissions'}
# -------------------------------------------------------------------------
@@ -667,7 +724,18 @@
return {None: 'Insufficient permissions'}
elif kwargs.get('isGroup'):
- return {None: 'Not yet implemented for groups'}
+ try:
+ protectedBranches = glEntity.protectedbranches.list(get_all=False)
+
+ if len(protectedBranches) == 0:
+ return {False: 'No protected branches found'}
+
+ return {True: 'Protected branches found'}
+
+ except (GitlabHttpError, GitlabGetError, GitlabAuthenticationError,
+ GitlabListError) as e:
+ if e.response_code in [401, 403]:
+ return {None: 'Insufficient permissions'}
# -------------------------------------------------------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gitlabcis-1.18.0/gitlabcis/cli/auth.py
new/gitlabcis-1.19.0/gitlabcis/cli/auth.py
--- old/gitlabcis-1.18.0/gitlabcis/cli/auth.py 2026-03-16 23:29:53.000000000
+0100
+++ new/gitlabcis-1.19.0/gitlabcis/cli/auth.py 2026-03-18 03:18:30.000000000
+0100
@@ -220,6 +220,7 @@
gitlab.exceptions.GitlabHttpError) as e:
if e.response_code == 404:
continue
+ continue
self.pathObjs.append({
'objectType': _flag,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/gitlabcis-1.18.0/gitlabcis/tests/unit/benchmarks/source_code_1/code_changes_1_1_test.py
new/gitlabcis-1.19.0/gitlabcis/tests/unit/benchmarks/source_code_1/code_changes_1_1_test.py
---
old/gitlabcis-1.18.0/gitlabcis/tests/unit/benchmarks/source_code_1/code_changes_1_1_test.py
2026-03-16 23:29:53.000000000 +0100
+++
new/gitlabcis-1.19.0/gitlabcis/tests/unit/benchmarks/source_code_1/code_changes_1_1_test.py
2026-03-18 03:18:30.000000000 +0100
@@ -137,29 +137,26 @@
test = code_changes_1_1.code_dismissal_restrictions
- kwargs = {'isProject': True}
- protectedBranches = Mock()
- branch = Mock()
- protectedBranches.list.return_value = [branch]
- glEntity.protectedbranches = protectedBranches
- run(glEntity, glObject, test, True, **kwargs)
+ for kwargs in [{'isProject': True}, {'isGroup': True}]:
+ protectedBranches = Mock()
+ branch = Mock()
+ protectedBranches.list.return_value = [branch]
+ glEntity.protectedbranches = protectedBranches
+ run(glEntity, glObject, test, True, **kwargs)
- protectedBranches.list.return_value = []
- glEntity.protectedbranches = protectedBranches
- run(glEntity, glObject, test, False, **kwargs)
+ protectedBranches.list.return_value = []
+ glEntity.protectedbranches = protectedBranches
+ run(glEntity, glObject, test, False, **kwargs)
- protectedBranches.list.return_value = []
- glEntity.protectedbranches.list.side_effect = GitlabHttpError(
- response_code=401)
- run(glEntity, glObject, test, None, **kwargs)
+ glEntity.protectedbranches.list.side_effect = GitlabHttpError(
+ response_code=401)
+ run(glEntity, glObject, test, None, **kwargs)
- glEntity.protectedbranches.list.side_effect = GitlabHttpError(
- 'Error', response_code=418)
- assert test(glEntity, glObject, **kwargs) is None
+ glEntity.protectedbranches.list.side_effect = GitlabHttpError(
+ 'Error', response_code=418)
+ assert test(glEntity, glObject, **kwargs) is None
- kwarg = [{'isGroup': True}, {'isInstance': True}]
- for kwargs in kwarg:
- run(glEntity, glObject, test, None, **kwargs)
+ run(glEntity, glObject, test, None, **{'isInstance': True})
# -----------------------------------------------------------------------------
@@ -238,9 +235,29 @@
'Error', response_code=418)
assert test(glEntity, glObject, **kwargs) is None
- kwarg = [{'isGroup': True}, {'isInstance': True}]
- for kwargs in kwarg:
- run(glEntity, glObject, test, None, **kwargs)
+ kwargs = {'isGroup': True}
+ branch = Mock()
+ branch.code_owner_approval_required = True
+ glEntity.protectedbranches.list.return_value = [branch]
+ glEntity.protectedbranches.list.side_effect = None
+ run(glEntity, glObject, test, True, **kwargs)
+
+ branch.code_owner_approval_required = False
+ glEntity.protectedbranches.list.return_value = [branch]
+ run(glEntity, glObject, test, False, **kwargs)
+
+ glEntity.protectedbranches.list.return_value = []
+ run(glEntity, glObject, test, False, **kwargs)
+
+ glEntity.protectedbranches.list.side_effect = GitlabHttpError(
+ response_code=403, error_message='403 Forbidden')
+ run(glEntity, glObject, test, None, **kwargs)
+
+ glEntity.protectedbranches.list.side_effect = GitlabHttpError(
+ response_code=404)
+ run(glEntity, glObject, test, False, **kwargs)
+
+ run(glEntity, glObject, test, None, **{'isInstance': True})
# -----------------------------------------------------------------------------
@@ -478,7 +495,23 @@
GitlabGetError(response_code=401)
run(unauthorised, unauthorised, test, None, **{'isInstance': True})
- run(glEntity, glObject, test, None, **{'isGroup': True})
+ kwargs = {'isGroup': True}
+ glEntity.protectedbranches.list.return_value = []
+ glEntity.protectedbranches.list.side_effect = None
+ run(glEntity, glObject, test, False, **kwargs)
+
+ branch = Mock()
+ branch.allow_force_push = True
+ glEntity.protectedbranches.list.return_value = [branch]
+ run(glEntity, glObject, test, False, **kwargs)
+
+ branch.allow_force_push = False
+ glEntity.protectedbranches.list.return_value = [branch]
+ run(glEntity, glObject, test, True, **kwargs)
+
+ unauthorised.protectedbranches.list.side_effect = \
+ GitlabGetError(response_code=401)
+ run(unauthorised, glObject, test, None, **kwargs)
# -----------------------------------------------------------------------------
@@ -521,7 +554,23 @@
GitlabGetError(response_code=401)
run(unauthorised, unauthorised, test, None, **{'isInstance': True})
- run(glEntity, glObject, test, None, **{'isGroup': True})
+ kwargs = {'isGroup': True}
+ glEntity.protectedbranches.list.return_value = []
+ glEntity.protectedbranches.list.side_effect = None
+ run(glEntity, glObject, test, False, **kwargs)
+
+ branch = Mock()
+ branch.allow_force_push = True
+ glEntity.protectedbranches.list.return_value = [branch]
+ run(glEntity, glObject, test, False, **kwargs)
+
+ branch.allow_force_push = False
+ glEntity.protectedbranches.list.return_value = [branch]
+ run(glEntity, glObject, test, True, **kwargs)
+
+ unauthorised.protectedbranches.list.side_effect = \
+ GitlabGetError(response_code=401)
+ run(unauthorised, glObject, test, None, **kwargs)
# -----------------------------------------------------------------------------
@@ -558,7 +607,17 @@
GitlabGetError(response_code=401)
run(unauthorised, unauthorised, test, None, **{'isInstance': True})
- run(glEntity, glObject, test, None, **{'isGroup': True})
+ kwargs = {'isGroup': True}
+ glEntity.protectedbranches.list.return_value = []
+ glEntity.protectedbranches.list.side_effect = None
+ run(glEntity, glObject, test, False, **kwargs)
+
+ glEntity.protectedbranches.list.return_value = ['main']
+ run(glEntity, glObject, test, True, **kwargs)
+
+ unauthorised.protectedbranches.list.side_effect = \
+ GitlabGetError(response_code=401)
+ run(unauthorised, glObject, test, None, **kwargs)
# -----------------------------------------------------------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gitlabcis-1.18.0/gitlabcis.egg-info/PKG-INFO
new/gitlabcis-1.19.0/gitlabcis.egg-info/PKG-INFO
--- old/gitlabcis-1.18.0/gitlabcis.egg-info/PKG-INFO 2026-03-16
23:30:20.000000000 +0100
+++ new/gitlabcis-1.19.0/gitlabcis.egg-info/PKG-INFO 2026-03-18
03:18:56.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: gitlabcis
-Version: 1.18.0
+Version: 1.19.0
Summary: An automated tool that assesses the GitLab CIS benchmarks against a
project.
Author-email: Nate Rosandich <[email protected]>, Neil McDonald
<[email protected]>, Mitra JozeNazemian
<[email protected]>
License: MIT License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gitlabcis-1.18.0/pyproject.toml
new/gitlabcis-1.19.0/pyproject.toml
--- old/gitlabcis-1.18.0/pyproject.toml 2026-03-16 23:30:10.000000000 +0100
+++ new/gitlabcis-1.19.0/pyproject.toml 2026-03-18 03:18:47.000000000 +0100
@@ -12,7 +12,7 @@
[project]
name = "gitlabcis"
-version = "1.18.0"
+version = "1.19.0"
requires-python = ">=3.10"
description = "An automated tool that assesses the GitLab CIS benchmarks
against a project."
authors = [