Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

Expand All @@ -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 |
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# CHANGELOG

## Content Management SDK For Python
---
## v1.11.3

#### 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.

---
## v1.11.2

Expand Down
2 changes: 1 addition & 1 deletion contentstack_management/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
4 changes: 2 additions & 2 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
2 changes: 1 addition & 1 deletion skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
4 changes: 2 additions & 2 deletions skills/dev-workflow/SKILL.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion skills/python-style/SKILL.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 4 additions & 8 deletions tests/api/users/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 7 additions & 4 deletions tests/integration/api/test_01_user.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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)

Expand Down
12 changes: 4 additions & 8 deletions tests/mock/users/test_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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()
Expand Down
7 changes: 2 additions & 5 deletions tests/unit/contentstack/test_totp_login.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import base64
import unittest
import os
import sys
Expand All @@ -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):
Expand All @@ -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}")
Expand Down Expand Up @@ -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
)

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/test_oauth_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json
import time
import unittest
import uuid
from unittest.mock import Mock, patch, MagicMock
import requests

Expand All @@ -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 = {}
Expand All @@ -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
Expand Down
Loading