From 2985fd91412705ce43368384fab848f1b50338b6 Mon Sep 17 00:00:00 2001 From: Aravind Kumar Date: Tue, 6 Oct 2026 17:46:22 +0530 Subject: [PATCH 1/6] Updated codeowners --- CODEOWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index ac7dad0..d9f2fb3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,4 +1,5 @@ * @contentstack/developer-ecosystem-pr-reviewers .github/workflows/ @contentstack/security-admin **/.snyk @contentstack/security-admin -**/CODEOWNERS @contentstack/security-admin \ No newline at end of file +**/CODEOWNERS @contentstack/security-admin +**/.gitguardian.yaml @contentstack/security-admin \ No newline at end of file From 8c708763d4221fc8a7e663cee5cbcf5453a72deb Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 26 Oct 2026 11:59:14 +0530 Subject: [PATCH 2/6] Fix: use approved placeholder Replace hardcoded passwords and secrets in tests with env/cred-helper values or per-run generated values, and drop the deepcode ignore suppression comments. Clears the Snyk Code NoHardcodedPasswords and HardcodedNonCryptoSecret findings. Co-Authored-By: Claude Sonnet 5.5 --- tests/api/users/test_api.py | 12 ++++-------- tests/integration/api/test_01_user.py | 11 +++++++---- tests/mock/users/test_mock.py | 12 ++++-------- tests/unit/contentstack/test_totp_login.py | 7 ++----- tests/unit/test_oauth_handler.py | 4 ++-- 5 files changed, 19 insertions(+), 27 deletions(-) diff --git a/tests/api/users/test_api.py b/tests/api/users/test_api.py index a25d78f..4b580fa 100644 --- a/tests/api/users/test_api.py +++ b/tests/api/users/test_api.py @@ -44,10 +44,8 @@ def test_active_user(self): "user": { "first_name": "your_first_name", "last_name": "your_last_name", - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - "password": "your_password", - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - "password_confirmation": "confirm_your_password" + "password": password, + "password_confirmation": password } } response = self.client.user().activate(activation_token, act_data) @@ -80,10 +78,8 @@ def test_reset_password(self): act_data = { "user": { "reset_password_token": "abcdefghijklmnop1234567890", - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - "password": "Simple@123", - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - "password_confirmation": "Simple@123" + "password": password, + "password_confirmation": password } } response = self.client.user().reset_password(act_data) diff --git a/tests/integration/api/test_01_user.py b/tests/integration/api/test_01_user.py index 05291f1..c82876f 100644 --- a/tests/integration/api/test_01_user.py +++ b/tests/integration/api/test_01_user.py @@ -1,11 +1,16 @@ """User API tests — profile fetch and update.""" +import os + import pytest from framework import helpers as h pytestmark = pytest.mark.order(1) +# Exercised only with bogus tokens; value comes from the environment, no fallback. +TEST_PASSWORD = os.environ.get("TEST_USER_PASSWORD") + class TestUser: def test_fetch(self, ctx): @@ -26,14 +31,12 @@ class TestUserAuthOps: """Account auth endpoints exercised safely (bogus tokens / non-real email).""" def test_activate_bogus_token(self, ctx): - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - resp = ctx.client.user().activate("bogus_activation_token", {"user": {"password": "Test@12345"}}) + resp = ctx.client.user().activate("bogus_activation_token", {"user": {"password": TEST_PASSWORD}}) h.assert_status(resp, 400, 404, 422) def test_reset_password_bogus_token(self, ctx): resp = ctx.client.user().reset_password( - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - {"user": {"reset_password_token": "bogus", "password": "Test@12345", "password_confirmation": "Test@12345"}} + {"user": {"reset_password_token": "bogus", "password": TEST_PASSWORD, "password_confirmation": TEST_PASSWORD}} ) h.assert_status(resp, 400, 404, 422) diff --git a/tests/mock/users/test_mock.py b/tests/mock/users/test_mock.py index 2507d71..0b833a9 100644 --- a/tests/mock/users/test_mock.py +++ b/tests/mock/users/test_mock.py @@ -38,10 +38,8 @@ def test_mock_active_user(self): "user": { "first_name": "your_first_name", "last_name": "your_last_name", - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - "password": "your_password", - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - "password_confirmation": "confirm_your_password" + "password": password, + "password_confirmation": password } } @@ -81,10 +79,8 @@ def test_reset_password(self): act_data = { "user": { "reset_password_token": "abcdefghijklmnop1234567890", - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - "password": "Simple@123", - # deepcode ignore NoHardcodedPasswords: test fixture value, not a real secret - "password_confirmation": "Simple@123" + "password": password, + "password_confirmation": password } } response = self.client.user().reset_password(act_data).json() diff --git a/tests/unit/contentstack/test_totp_login.py b/tests/unit/contentstack/test_totp_login.py index 3f6a049..4a84dbe 100644 --- a/tests/unit/contentstack/test_totp_login.py +++ b/tests/unit/contentstack/test_totp_login.py @@ -1,3 +1,4 @@ +import base64 import unittest import os import sys @@ -18,8 +19,7 @@ def setUp(self): self.client = Client() self.test_email = "test@example.com" self.test_password = "test_password" - # deepcode ignore HardcodedNonCryptoSecret: test fixture value, not a real secret - self.test_secret = "JBSWY3DPEHPK3PXP" # Standard test secret for TOTP + self.test_secret = base64.b32encode(os.urandom(10)).decode() # Valid base32 TOTP secret generated per test run self.test_tfa_token = "123456" def tearDown(self): @@ -38,9 +38,7 @@ def test_login_method_signature_with_totp(self): # Test that the method accepts TOTP parameters without error try: client.login(self.test_email, self.test_password, tfa_token=self.test_tfa_token) - # deepcode ignore HardcodedNonCryptoSecret: test fixture value, not a real secret client.login(self.test_email, self.test_password, mfa_secret=self.test_secret) - # deepcode ignore HardcodedNonCryptoSecret: test fixture value, not a real secret client.login(self.test_email, self.test_password, tfa_token=self.test_tfa_token, mfa_secret=self.test_secret) except Exception as e: self.fail(f"Login method should accept TOTP parameters without error: {e}") @@ -74,7 +72,6 @@ def test_login_with_mfa_secret_generates_totp(self): result = self.client.login( self.test_email, self.test_password, - # deepcode ignore HardcodedNonCryptoSecret: test fixture value, not a real secret mfa_secret=self.test_secret ) diff --git a/tests/unit/test_oauth_handler.py b/tests/unit/test_oauth_handler.py index 1ff20e9..c14507a 100644 --- a/tests/unit/test_oauth_handler.py +++ b/tests/unit/test_oauth_handler.py @@ -5,6 +5,7 @@ import json import time import unittest +import uuid from unittest.mock import Mock, patch, MagicMock import requests @@ -19,7 +20,7 @@ def setUp(self): self.app_id = "test-app-id" self.client_id = "test-client-id" self.redirect_uri = "http://localhost:3000/callback" - self.client_secret = "test-client-secret" + self.client_secret = uuid.uuid4().hex # Generated per test run, not a real secret self.scope = ["read", "write"] self.api_client = Mock() self.api_client.headers = {} @@ -31,7 +32,6 @@ def setUp(self): app_id=self.app_id, client_id=self.client_id, redirect_uri=self.redirect_uri, - # deepcode ignore HardcodedNonCryptoSecret: test fixture value, not a real secret client_secret=self.client_secret, scope=self.scope, api_client=self.api_client From 1b79cbc777e22d04a2b833b7ee3ceb65a7ea239f Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 26 Oct 2026 12:25:19 +0530 Subject: [PATCH 3/6] Fix: bump urllib3 floor to 2.8.0 urllib3 2.7.0 is affected by improper certificate validation, resource allocation without limits and an infinite loop; 2.8.0 fixes all three. Raise the minimum so the vulnerable version is no longer installable. Co-Authored-By: Claude Sonnet 5.5 --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index e9b1a36..fc2ba15 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ python-dotenv>=1.2.2 # <82: pkg_resources (used by coverage-badge and similar tools) was removed in setuptools 82+ setuptools>=83.0.0 requests>=2.33.0,<3.0.0 -urllib3>=2.7.0,<3.0.0 +urllib3>=2.8.0,<3.0.0 requests-toolbelt>=1.0.0,<2.0.0 pyotp==2.9.0 packaging>=24.0 diff --git a/setup.py b/setup.py index 53a99b3..fdfaccf 100644 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ def get_author_email(package): install_requires=[ "requests >= 2.5.4", "requests-toolbelt >= 0.3.1", - "urllib3 >= 2.7.0,<3.0.0", + "urllib3 >= 2.8.0,<3.0.0", ], extras_require={ "dev": ["pytest>=7.0", "twine>=4.0.2", "packaging>=24.0", "dotenv>=0.0.5", "pylint>=2.0.0"], From ba9b3a9a8f027943b0c722e672ccfaa9f3a8dcf2 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 26 Oct 2026 12:29:34 +0530 Subject: [PATCH 4/6] chore: bump version to 1.11.3 Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 8 ++++++++ contentstack_management/__init__.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9539610..b9d6462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # CHANGELOG ## Content Management SDK For Python +--- +## v1.11.3 + +#### Date: 26 October 2026 + +- Bumped `urllib3` minimum version from `>=2.7.0` to `>=2.8.0` in `requirements.txt` and `setup.py` to resolve Snyk findings: improper certificate validation, allocation of resources without limits or throttling, and an infinite loop (DX-27815, DX-27816, DX-27817). +- Tests: replaced hardcoded passwords and secrets with environment-backed or runtime-generated values and removed `# deepcode ignore` suppression comments, resolving Snyk Code findings (DX-22180 to DX-22195). No change to the published package. + --- ## v1.11.2 diff --git a/contentstack_management/__init__.py b/contentstack_management/__init__.py index e038906..9cb9f32 100644 --- a/contentstack_management/__init__.py +++ b/contentstack_management/__init__.py @@ -102,7 +102,7 @@ def get_contentstack_endpoint(region='us', service='', omit_https=False): __author__ = 'dev-ex' __status__ = 'debug' __region__ = 'na' -__version__ = '1.11.2' +__version__ = '1.11.3' __host__ = 'api.contentstack.io' __protocol__ = 'https://' __api_version__ = 'v3' From 75d96fff1434dd828c5fc184681ac1652f5655f7 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Thu, 29 Oct 2026 12:34:53 +0530 Subject: [PATCH 5/6] chore: set v1.11.3 release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9d6462..1709e8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ --- ## v1.11.3 -#### Date: 26 October 2026 +#### Date: 02 November 2026 - Bumped `urllib3` minimum version from `>=2.7.0` to `>=2.8.0` in `requirements.txt` and `setup.py` to resolve Snyk findings: improper certificate validation, allocation of resources without limits or throttling, and an infinite loop (DX-27815, DX-27816, DX-27817). - Tests: replaced hardcoded passwords and secrets with environment-backed or runtime-generated values and removed `# deepcode ignore` suppression comments, resolving Snyk Code findings (DX-22180 to DX-22195). No change to the published package. From 5b35a278c626a231c22b980133068cb7721c1e03 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 26 Oct 2026 16:27:52 +0530 Subject: [PATCH 6/6] Fix: replace pylint with ruff in dev requirements Remove GPL/LGPL dev-only dependencies (pylint, astroid); update docs. Co-Authored-By: Claude Sonnet 5.5 --- AGENTS.md | 6 +++--- requirements-dev.txt | 4 ++-- skills/README.md | 2 +- skills/dev-workflow/SKILL.md | 4 ++-- skills/python-style/SKILL.md | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3d3c39f..6396b56 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ | Build | `setuptools` / `setup.py`; package `contentstack_management` | | HTTP | `requests`, `requests-toolbelt`, `urllib3` | | Tests | `pytest` — `tests/integration` (live e2e / sanity, dynamic stack), `tests/unit`, `tests/mock`, `tests/api` (legacy, superseded by `tests/integration`) | -| Lint / coverage | `pylint`, `coverage` (see `requirements-dev.txt`) | +| Lint / coverage | `ruff`, `coverage` (see `requirements-dev.txt`) | | Secrets / hooks | Talisman, Snyk (see `README.md` development setup) | ## Source layout @@ -44,7 +44,7 @@ | Test (mock) | `pytest tests/mock/ -v` | | Test (legacy API, live) | `pytest tests/api/ -v` (needs `.env` — see `tests/cred.py`) | | Coverage (CI) | `coverage run -m pytest tests/unit/` | -| Lint | `pylint contentstack_management/` | +| Lint | `ruff check contentstack_management/` | > **CI note:** `.github/workflows/unit-test.yml` runs **only `tests/unit/`** (no credentials). The `tests/integration` sanity suite is run manually (or via a credential-gated job) because it provisions real stacks. @@ -70,7 +70,7 @@ Do not commit secrets. `tests/integration/.env`, `docs/`, and the repo-root `cma | Skill | Path | What it covers | |-------|------|----------------| -| **Development workflow** | [`skills/dev-workflow/SKILL.md`](skills/dev-workflow/SKILL.md) | Install, pytest suites, packaging version, pylint, hooks, PR baseline | +| **Development workflow** | [`skills/dev-workflow/SKILL.md`](skills/dev-workflow/SKILL.md) | Install, pytest suites, packaging version, ruff, hooks, PR baseline | | **Contentstack Management (SDK)** | [`skills/contentstack-management/SKILL.md`](skills/contentstack-management/SKILL.md) | **`Client`**, **`Stack`**, **`_APIClient`**, CMA resources, OAuth, CMA docs | | **Python style & repo layout** | [`skills/python-style/SKILL.md`](skills/python-style/SKILL.md) | Package layout, naming, imports via **`_APIClient`**, secrets in logs | | **Testing** | [`skills/testing/SKILL.md`](skills/testing/SKILL.md) | pytest unit / API / mock, **`tests/cred.py`**, env hygiene | diff --git a/requirements-dev.txt b/requirements-dev.txt index 8188ec2..4f32c56 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ # Development-only dependencies — not shipped in the production package. -# Keep linting/tools (e.g. pylint) here so production scans can exclude them. +# Keep linting/tools (e.g. ruff) here so production scans can exclude them. -r requirements.txt -pylint>=2.0.0 +ruff>=0.11.5 diff --git a/skills/README.md b/skills/README.md index ef2eea6..91dcf80 100644 --- a/skills/README.md +++ b/skills/README.md @@ -6,7 +6,7 @@ Source of truth for detailed guidance. Read **[`AGENTS.md`](../AGENTS.md)** firs | Skill folder | Use when | |--------------|----------| -| **dev-workflow** | Install, **`pytest`** (unit / API / mock), **`__version__`**, pylint, Talisman/Snyk, before PR | +| **dev-workflow** | Install, **`pytest`** (unit / API / mock), **`__version__`**, ruff, Talisman/Snyk, before PR | | **contentstack-management** | **`Client`**, **`Stack`**, **`_APIClient`**, domain modules, OAuth, CMA paths and payloads | | **python-style** | Editing **`contentstack_management/`** or **`setup.py`** / **`requirements.txt`**—layout, style, imports | | **testing** | Adding or changing tests under **`tests/`**, **`tests/cred.py`**, env for API runs | diff --git a/skills/dev-workflow/SKILL.md b/skills/dev-workflow/SKILL.md index 47c3355..4ae0f9d 100644 --- a/skills/dev-workflow/SKILL.md +++ b/skills/dev-workflow/SKILL.md @@ -1,6 +1,6 @@ --- name: dev-workflow -description: Use for install, pytest suites, versioning, pylint, hooks, and PR baseline in contentstack-management-python. +description: Use for install, pytest suites, versioning, ruff, hooks, and PR baseline in contentstack-management-python. --- # Development workflow – Contentstack Management Python @@ -30,7 +30,7 @@ description: Use for install, pytest suites, versioning, pylint, hooks, and PR b ### Tooling -- **pylint** is listed in **`requirements-dev.txt`**; follow existing style in touched files. +- **ruff** is listed in **`requirements-dev.txt`** (lint with `ruff check contentstack_management/`); follow existing style in touched files. - **Husky / Talisman / Snyk** — see **README.md** for local hook setup. ### Pull requests diff --git a/skills/python-style/SKILL.md b/skills/python-style/SKILL.md index d91ed70..0abc62a 100644 --- a/skills/python-style/SKILL.md +++ b/skills/python-style/SKILL.md @@ -1,6 +1,6 @@ --- name: python-style -description: Use for package layout under contentstack_management/, setup.py, pylint-friendly style, and imports. +description: Use for package layout under contentstack_management/, setup.py, lint-friendly style, and imports. --- # Python style and repo layout – Contentstack Management Python