upwork-learning 0.1.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 (78) hide show
  1. upwork_learning-0.1.0/.claude/hooks/stop-preflight.sh +30 -0
  2. upwork_learning-0.1.0/.claude/settings.json +17 -0
  3. upwork_learning-0.1.0/.env.example +24 -0
  4. upwork_learning-0.1.0/.githooks/pre-commit +18 -0
  5. upwork_learning-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  6. upwork_learning-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +13 -0
  7. upwork_learning-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +28 -0
  8. upwork_learning-0.1.0/.github/workflows/ci.yml +93 -0
  9. upwork_learning-0.1.0/.github/workflows/release.yml +72 -0
  10. upwork_learning-0.1.0/.github/workflows/secret-scan.yml +22 -0
  11. upwork_learning-0.1.0/.gitignore +143 -0
  12. upwork_learning-0.1.0/.gitleaks.toml +171 -0
  13. upwork_learning-0.1.0/.pre-commit-config.yaml +54 -0
  14. upwork_learning-0.1.0/CHANGELOG.md +37 -0
  15. upwork_learning-0.1.0/CLAUDE.md +125 -0
  16. upwork_learning-0.1.0/CONTRIBUTING.md +85 -0
  17. upwork_learning-0.1.0/LICENSE +21 -0
  18. upwork_learning-0.1.0/Makefile +34 -0
  19. upwork_learning-0.1.0/PKG-INFO +247 -0
  20. upwork_learning-0.1.0/README.md +201 -0
  21. upwork_learning-0.1.0/ROADMAP.md +384 -0
  22. upwork_learning-0.1.0/config/.gitignore +34 -0
  23. upwork_learning-0.1.0/docs/cli/email.md +64 -0
  24. upwork_learning-0.1.0/docs/cli/index.md +52 -0
  25. upwork_learning-0.1.0/docs/cli/pdf.md +81 -0
  26. upwork_learning-0.1.0/docs/cli/sheets.md +72 -0
  27. upwork_learning-0.1.0/docs/development/contributing.md +76 -0
  28. upwork_learning-0.1.0/docs/development/testing.md +110 -0
  29. upwork_learning-0.1.0/docs/development/type-checking.md +101 -0
  30. upwork_learning-0.1.0/docs/examples/bol-com.md +52 -0
  31. upwork_learning-0.1.0/docs/examples/workflows.md +132 -0
  32. upwork_learning-0.1.0/docs/getting-started/configuration.md +56 -0
  33. upwork_learning-0.1.0/docs/getting-started/installation.md +53 -0
  34. upwork_learning-0.1.0/docs/getting-started/quick-start.md +96 -0
  35. upwork_learning-0.1.0/docs/index.md +64 -0
  36. upwork_learning-0.1.0/docs/integrations/email.md +87 -0
  37. upwork_learning-0.1.0/docs/integrations/google-sheets.md +67 -0
  38. upwork_learning-0.1.0/docs/integrations/pdf-processing.md +77 -0
  39. upwork_learning-0.1.0/docs/runbook.md +239 -0
  40. upwork_learning-0.1.0/docs/stylesheets/extra.css +11 -0
  41. upwork_learning-0.1.0/examples/__init__.py +1 -0
  42. upwork_learning-0.1.0/examples/auto_email.py +193 -0
  43. upwork_learning-0.1.0/examples/bol_com/README.md +53 -0
  44. upwork_learning-0.1.0/examples/bol_com/__init__.py +15 -0
  45. upwork_learning-0.1.0/examples/bol_com/cli.py +166 -0
  46. upwork_learning-0.1.0/examples/bol_com/client.py +322 -0
  47. upwork_learning-0.1.0/examples/bol_com/config.py +29 -0
  48. upwork_learning-0.1.0/examples/bol_com/sync.py +107 -0
  49. upwork_learning-0.1.0/examples/pdf_to_sheets.py +113 -0
  50. upwork_learning-0.1.0/mkdocs.yml +86 -0
  51. upwork_learning-0.1.0/noxfile.py +279 -0
  52. upwork_learning-0.1.0/preflight.sh +83 -0
  53. upwork_learning-0.1.0/pyproject.toml +146 -0
  54. upwork_learning-0.1.0/src/__init__.py +3 -0
  55. upwork_learning-0.1.0/src/cli.py +349 -0
  56. upwork_learning-0.1.0/src/integrations/__init__.py +9 -0
  57. upwork_learning-0.1.0/src/integrations/base.py +237 -0
  58. upwork_learning-0.1.0/src/integrations/email_handler.py +514 -0
  59. upwork_learning-0.1.0/src/integrations/google_sheets.py +435 -0
  60. upwork_learning-0.1.0/src/integrations/pdf_processor.py +372 -0
  61. upwork_learning-0.1.0/src/utils/__init__.py +12 -0
  62. upwork_learning-0.1.0/src/utils/config.py +133 -0
  63. upwork_learning-0.1.0/src/utils/logger.py +103 -0
  64. upwork_learning-0.1.0/src/utils/retry.py +84 -0
  65. upwork_learning-0.1.0/tests/__init__.py +1 -0
  66. upwork_learning-0.1.0/tests/fixtures/credentials.json +10 -0
  67. upwork_learning-0.1.0/tests/integrations/__init__.py +1 -0
  68. upwork_learning-0.1.0/tests/integrations/test_base.py +185 -0
  69. upwork_learning-0.1.0/tests/integrations/test_email_handler.py +449 -0
  70. upwork_learning-0.1.0/tests/integrations/test_google_sheets.py +428 -0
  71. upwork_learning-0.1.0/tests/integrations/test_pdf_processor.py +286 -0
  72. upwork_learning-0.1.0/tests/test_cli.py +548 -0
  73. upwork_learning-0.1.0/tests/utils/__init__.py +1 -0
  74. upwork_learning-0.1.0/tests/utils/test_config.py +62 -0
  75. upwork_learning-0.1.0/tests/utils/test_logger.py +76 -0
  76. upwork_learning-0.1.0/tests/utils/test_retry.py +128 -0
  77. upwork_learning-0.1.0/tsconfig.json +16 -0
  78. upwork_learning-0.1.0/uv.lock +1855 -0
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # .claude/hooks/stop-preflight.sh
3
+ # Stop hook: запускает preflight.sh, результат возвращает как JSON systemMessage.
4
+ # Если lint или pytest упали — устанавливает continue:false, чтобы Claude увидел ошибки.
5
+
6
+ cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
7
+
8
+ # Быстрый выход без проверок: SKIP_PREFLIGHT=1 claude
9
+ if [ "${SKIP_PREFLIGHT:-0}" = "1" ]; then
10
+ echo '{"systemMessage": "⚡ Preflight skipped (SKIP_PREFLIGHT=1)"}'
11
+ exit 0
12
+ fi
13
+
14
+ OUTPUT=$(bash preflight.sh 2>&1)
15
+ CODE=$?
16
+
17
+ # Выводим JSON для Claude Code
18
+ python3 - "$OUTPUT" "$CODE" <<'PY'
19
+ import json, sys
20
+
21
+ output = sys.argv[1]
22
+ code = int(sys.argv[2])
23
+
24
+ result = {"systemMessage": output}
25
+ if code != 0:
26
+ result["continue"] = False
27
+ result["stopReason"] = "Preflight failed — исправь ошибки перед завершением"
28
+
29
+ print(json.dumps(result))
30
+ PY
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
+ "hooks": {
4
+ "Stop": [
5
+ {
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "bash .claude/hooks/stop-preflight.sh",
10
+ "timeout": 120,
11
+ "statusMessage": "Running preflight: lint · mypy · pytest …"
12
+ }
13
+ ]
14
+ }
15
+ ]
16
+ }
17
+ }
@@ -0,0 +1,24 @@
1
+ # Google Sheets Integration
2
+ GOOGLE_SHEETS_CREDENTIALS_PATH=config/credentials.json
3
+ GOOGLE_SHEETS_SPREADSHEET_ID=your_spreadsheet_id_here
4
+
5
+ # Email Configuration
6
+ SMTP_HOST=smtp.gmail.com
7
+ SMTP_PORT=587
8
+ SMTP_USER=your_email@gmail.com
9
+ SMTP_PASSWORD=your_app_password
10
+ IMAP_HOST=imap.gmail.com
11
+ IMAP_PORT=993
12
+ IMAP_USER=your_email@gmail.com
13
+ IMAP_PASSWORD=your_app_password
14
+
15
+ # API Keys
16
+ BOL_COM_API_KEY=your_bol_com_api_key
17
+ BOL_COM_CLIENT_ID=your_client_id
18
+ BOL_COM_CLIENT_SECRET=your_client_secret
19
+
20
+ # Application Settings
21
+ LOG_LEVEL=INFO
22
+ MAX_RETRIES=3
23
+ RATE_LIMIT_DELAY=1.0
24
+ ENVIRONMENT=development
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+ # Pre-commit hook to run linting and type checking
3
+
4
+ echo "Running pre-commit checks..."
5
+
6
+ # Run ruff
7
+ echo "Running ruff..."
8
+ ruff check src/
9
+
10
+ # Run mypy
11
+ echo "Running mypy..."
12
+ mypy src/
13
+
14
+ # Run tests
15
+ echo "Running tests..."
16
+ pytest tests/
17
+
18
+ echo "Pre-commit checks complete!"
@@ -0,0 +1,23 @@
1
+ ## Bug Report
2
+
3
+ **Description**
4
+ A clear description of the bug.
5
+
6
+ **Steps to Reproduce**
7
+ 1. Go to '...'
8
+ 2. Click on '....'
9
+ 3. See error
10
+
11
+ **Expected Behavior**
12
+ What you expected to happen.
13
+
14
+ **Actual Behavior**
15
+ What actually happened.
16
+
17
+ **Environment**
18
+ - OS: [e.g., Windows 11]
19
+ - Python version: [e.g., 3.11]
20
+ - Package version: [e.g., 0.1.0]
21
+
22
+ **Additional Context**
23
+ Any other context about the problem.
@@ -0,0 +1,13 @@
1
+ ## Feature Request
2
+
3
+ **Is your feature request related to a problem? Please describe.**
4
+ A clear description of the problem.
5
+
6
+ **Describe the solution you'd like**
7
+ A clear description of what you want to happen.
8
+
9
+ **Describe alternatives you've considered**
10
+ Any alternative solutions you've considered.
11
+
12
+ **Additional context**
13
+ Any other context about the feature request.
@@ -0,0 +1,28 @@
1
+ ## Pull Request Description
2
+
3
+ ### Type of Change
4
+
5
+ - [ ] Bug fix
6
+ - [ ] New feature
7
+ - [ ] Documentation update
8
+ - [ ] Code refactoring
9
+ - [ ] Test update
10
+
11
+ ### Checklist
12
+
13
+ - [ ] My code follows the style guidelines of this project
14
+ - [ ] I have performed a self-review of my own code
15
+ - [ ] I have commented my code, particularly in hard-to-understand areas
16
+ - [ ] I have made corresponding changes to the documentation
17
+ - [ ] My changes generate no new warnings
18
+ - [ ] I have added tests that prove my fix is effective or that my feature works
19
+ - [ ] New and existing unit tests pass locally with my changes
20
+ - [ ] Any dependent changes have been merged and published
21
+
22
+ ### Description
23
+
24
+ Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
25
+
26
+ ### Related Issues
27
+
28
+ Fixes #(issue number)
@@ -0,0 +1,93 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ branches: [main, master]
7
+
8
+ jobs:
9
+ test:
10
+ name: Test
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v4
21
+
22
+ - name: Set up Python ${{ matrix.python-version }}
23
+ run: uv python install ${{ matrix.python-version }}
24
+
25
+ - name: Create virtual environment
26
+ run: uv venv
27
+
28
+ - name: Install dependencies
29
+ run: uv pip install -e ".[dev,security]" --python .venv/bin/python
30
+
31
+ - name: Lint with ruff
32
+ run: .venv/bin/ruff check src/ tests/
33
+
34
+ - name: Check formatting
35
+ run: .venv/bin/ruff format --check src/ tests/
36
+
37
+ - name: Type check with mypy
38
+ run: .venv/bin/mypy src/
39
+
40
+ - name: Run tests
41
+ run: .venv/bin/pytest --cov=src --cov-report=xml
42
+
43
+ - name: Upload coverage to Codecov
44
+ uses: codecov/codecov-action@v4
45
+ with:
46
+ file: ./coverage.xml
47
+ fail_ci_if_error: false
48
+
49
+ audit:
50
+ name: Dependency Audit
51
+ runs-on: ubuntu-latest
52
+
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - name: Install uv
57
+ uses: astral-sh/setup-uv@v4
58
+
59
+ - name: Set up Python
60
+ run: uv python install 3.12
61
+
62
+ - name: Create virtual environment
63
+ run: uv venv
64
+
65
+ - name: Install dependencies
66
+ run: uv pip install -e ".[dev]" --python .venv/bin/python
67
+
68
+ - name: Audit dependencies for known vulnerabilities
69
+ # CVE-2026-4539 (pygments): no patched release available yet; pinned to 2.19.2 in pyproject.toml
70
+ run: uv pip install pip-audit --python .venv/bin/python && .venv/bin/pip-audit --ignore-vuln CVE-2026-4539
71
+
72
+ docs:
73
+ name: Docs
74
+ runs-on: ubuntu-latest
75
+ if: github.ref == 'refs/heads/main'
76
+
77
+ steps:
78
+ - uses: actions/checkout@v4
79
+
80
+ - name: Install uv
81
+ uses: astral-sh/setup-uv@v4
82
+
83
+ - name: Set up Python
84
+ run: uv python install 3.12
85
+
86
+ - name: Create virtual environment
87
+ run: uv venv
88
+
89
+ - name: Install docs dependencies
90
+ run: uv pip install --python .venv/bin/python mkdocs-material mike
91
+
92
+ - name: Deploy documentation
93
+ run: uv run mkdocs gh-deploy --force
@@ -0,0 +1,72 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ # OIDC Trusted Publishing — no API token stored in secrets.
9
+ # One-time setup required on PyPI:
10
+ # https://pypi.org/manage/account/publishing/
11
+ # Publisher: petro-nazarenko / Agent-Guidelines-for-Upwork-Learning-Projects
12
+ # Workflow: release.yml | Environment: (leave blank)
13
+ permissions:
14
+ contents: write # create GitHub release + upload assets
15
+ id-token: write # OIDC token for PyPI trusted publishing
16
+
17
+ jobs:
18
+ build:
19
+ name: Build
20
+ runs-on: ubuntu-latest
21
+
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@v4
27
+
28
+ - name: Set up Python
29
+ run: uv python install 3.12
30
+
31
+ - name: Build package
32
+ run: uv build
33
+
34
+ - name: Upload dist artifacts
35
+ uses: actions/upload-artifact@v4
36
+ with:
37
+ name: dist
38
+ path: dist/
39
+
40
+ publish:
41
+ name: Publish to PyPI
42
+ runs-on: ubuntu-latest
43
+ needs: build
44
+
45
+ steps:
46
+ - name: Download dist artifacts
47
+ uses: actions/download-artifact@v4
48
+ with:
49
+ name: dist
50
+ path: dist/
51
+
52
+ - name: Publish to PyPI (trusted publishing)
53
+ uses: pypa/gh-action-pypi-publish@release/v1
54
+
55
+ github-release:
56
+ name: Create GitHub Release
57
+ runs-on: ubuntu-latest
58
+ needs: build
59
+
60
+ steps:
61
+ - name: Download dist artifacts
62
+ uses: actions/download-artifact@v4
63
+ with:
64
+ name: dist
65
+ path: dist/
66
+
67
+ - name: Create GitHub Release
68
+ uses: softprops/action-gh-release@v1
69
+ with:
70
+ files: dist/*
71
+ env:
72
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,22 @@
1
+ name: Secret Scanning
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ branches: [main, master]
7
+
8
+ jobs:
9
+ gitleaks:
10
+ name: Gitleaks Scan
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout code
14
+ uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - name: Run Gitleaks
19
+ uses: gitleaks/gitleaks-action@v2
20
+ env:
21
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22
+ GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
@@ -0,0 +1,143 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # PyInstaller
28
+ *.manifest
29
+ *.spec
30
+
31
+ # Installer logs
32
+ pip-log.txt
33
+ pip-delete-this-directory.txt
34
+
35
+ # Unit test / coverage reports
36
+ htmlcov/
37
+ .tox/
38
+ .nox/
39
+ .coverage
40
+ .coverage.*
41
+ .cache
42
+ nosetests.xml
43
+ coverage.xml
44
+ *.cover
45
+ *.py,cover
46
+ .hypothesis/
47
+ .pytest_cache/
48
+
49
+ # Translations
50
+ *.mo
51
+ *.pot
52
+
53
+ # Django stuff:
54
+ *.log
55
+ local_settings.py
56
+
57
+ # Flask stuff:
58
+ instance/
59
+ .webassets-cache
60
+
61
+ # Scrapy stuff:
62
+ .scrapy
63
+
64
+ # Sphinx documentation
65
+ docs/_build/
66
+
67
+ # PyBuilder
68
+ target/
69
+
70
+ # Jupyter Notebook
71
+ .ipynb_checkpoints
72
+
73
+ # IPython
74
+ profile_default/
75
+ ipython_config.py
76
+
77
+ # pyenv
78
+ .python-version
79
+
80
+ # pipenv
81
+ Pipfile.lock
82
+
83
+ # uv
84
+ # uv.lock is intentionally tracked — see ROADMAP.md §5.6
85
+
86
+ # poetry
87
+ poetry.lock
88
+
89
+ # PEP 582
90
+ __pypackages__/
91
+
92
+ # Celery stuff
93
+ celerybeat-schedule
94
+ celerybeat.pid
95
+
96
+ # SageMath parsed files
97
+ *.sage.py
98
+
99
+ # Environments
100
+ .env
101
+ .venv
102
+ env/
103
+ venv/
104
+ ENV/
105
+ env.bak/
106
+ venv.bak/
107
+
108
+ # Spyder project settings
109
+ .spyderproject
110
+ .spyproject
111
+
112
+ # Rope project settings
113
+ .ropeproject
114
+
115
+ # mkdocs documentation
116
+ /site
117
+
118
+ # mypy
119
+ .mypy_cache/
120
+ .dmypy.json
121
+ dmypy.json
122
+
123
+ # Pyre type checker
124
+ .pyre/
125
+
126
+ # IDE
127
+ .idea/
128
+ .vscode/
129
+ *.swp
130
+ *.swo
131
+
132
+ # OS
133
+ .DS_Store
134
+ Thumbs.db
135
+
136
+ # Project specific
137
+ logs/
138
+ data/
139
+ config/credentials.json
140
+ *.sample.json
141
+
142
+ # Claude Code local settings (personal overrides)
143
+ .claude/settings.local.json
@@ -0,0 +1,171 @@
1
+ # Gitleaks configuration
2
+ # https://github.com/gitleaks/gitleaks
3
+
4
+ [allowlist]
5
+ description = "Allowlisted paths and patterns"
6
+ paths = [
7
+ "^examples?/",
8
+ "^docs?/",
9
+ "^config/samples?/",
10
+ "^\\.venv/",
11
+ "^venv/",
12
+ "^dist/",
13
+ "^build/",
14
+ "^\\.git/",
15
+ # Fixture files contain intentionally fake keys for unit tests
16
+ "^tests/fixtures/",
17
+ ]
18
+ regexes = [
19
+ "^ пример? ",
20
+ "^ example ",
21
+ "^placeholder[_-]?",
22
+ "^test[_-]?",
23
+ # Python type annotations — field definitions, not real secrets
24
+ # e.g. smtp_password: SecretStr | None = None
25
+ "^SecretStr",
26
+ "^str \\| None",
27
+ "^None$",
28
+ ]
29
+
30
+ [[rules]]
31
+ id = "aws-access-key"
32
+ description = "AWS Access Key"
33
+ regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}'''
34
+ tags = ["key", "AWS"]
35
+
36
+ [[rules]]
37
+ id = "aws-secret-access-key"
38
+ description = "AWS Secret Access Key"
39
+ regex = '''(?i)aws[_-]?secret[_-]?access[_-]?key\s*[:=]\s*['"]?[A-Za-z0-9/+=]{40}['"]?'''
40
+ tags = ["key", "AWS"]
41
+
42
+ [[rules]]
43
+ id = "generic-api-key"
44
+ description = "Generic API Key"
45
+ regex = '''(?i)(api[_-]?key|apikey)\s*[:=]\s*['"]?[a-zA-Z0-9]{16,64}['"]?'''
46
+ tags = ["key", "API"]
47
+
48
+ [[rules]]
49
+ id = "generic-secret-key"
50
+ description = "Generic Secret Key"
51
+ regex = '''(?i)(secret[_-]?key|private[_-]?key|client[_-]?secret)\s*[:=]\s*['"]?[a-zA-Z0-9+/=]{20,64}['"]?'''
52
+ tags = ["key", "secret"]
53
+
54
+ [[rules]]
55
+ id = "google-api-key"
56
+ description = "Google API Key"
57
+ regex = '''AIza[0-9A-Za-z\\-_]{35}'''
58
+ tags = ["key", "Google"]
59
+
60
+ [[rules]]
61
+ id = "google-oauth-access-token"
62
+ description = "Google OAuth Access Token"
63
+ regex = '''ya29\.[0-9A-Za-z\\-_]+'''
64
+ tags = ["key", "Google", "OAuth"]
65
+
66
+ [[rules]]
67
+ id = "github-token"
68
+ description = "GitHub Token"
69
+ regex = '''gh[pousr]_[A-Za-z0-9_]{36,255}'''
70
+ tags = ["key", "GitHub"]
71
+
72
+ [[rules]]
73
+ id = "gitlab-personal-access-token"
74
+ description = "GitLab Personal Access Token"
75
+ regex = '''glpat-[0-9a-zA-Z\\-_]{20}'''
76
+ tags = ["key", "GitLab"]
77
+
78
+ [[rules]]
79
+ id = "slack-token"
80
+ description = "Slack Token"
81
+ regex = '''xox[baprs]-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*'''
82
+ tags = ["key", "Slack"]
83
+
84
+ [[rules]]
85
+ id = "stripe-api-key"
86
+ description = "Stripe API Key"
87
+ regex = '''sk_live_[0-9a-zA-Z]{24,32}'''
88
+ tags = ["key", "Stripe"]
89
+
90
+ [[rules]]
91
+ id = "stripe-publishable-key"
92
+ description = "Stripe Publishable Key"
93
+ regex = '''pk_live_[0-9a-zA-Z]{24,32}'''
94
+ tags = ["key", "Stripe"]
95
+
96
+ [[rules]]
97
+ id = "ssh-private-key"
98
+ description = "SSH Private Key"
99
+ regex = '''-----BEGIN (RSA |DSA |EC |OPENSSH |PGP )?PRIVATE KEY-----'''
100
+ tags = ["key", "SSH"]
101
+
102
+ [rules.allowlist]
103
+ # tests/fixtures/credentials.json contains a deliberately fake RSA key for unit tests
104
+ paths = ['''tests/fixtures/''']
105
+
106
+ [[rules]]
107
+ id = "email-password-credentials"
108
+ description = "Email/Password credentials"
109
+ regex = '''(?i)(smtp|imap|email)[_-]?(password|passwd|pwd)\s*[:=]\s*['"]?[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{}|;:,.<>?]{6,}['"]?'''
110
+ tags = ["credentials", "email"]
111
+
112
+ [rules.allowlist]
113
+ # regexTarget="line" lets us match the whole source line so we can distinguish:
114
+ # field definition: smtp_password: SecretStr | None = None ← allowed
115
+ # real assignment: smtp_password = "actualpassword" ← blocked
116
+ regexTarget = "line"
117
+ regexes = [
118
+ # Type annotations in dataclasses / Pydantic settings (colon, not equals)
119
+ '''(?i)(smtp|imap)[_-]?password\s*:\s*SecretStr''',
120
+ # Module-level test constant — value comes from os.environ, not a literal
121
+ '''_TEST_SECRET\s*=\s*SecretStr\(os\.environ''',
122
+ # Call sites that reference the constant (no string literal on the line)
123
+ '''(smtp|imap)_password\s*=\s*_TEST_SECRET''',
124
+ ]
125
+
126
+ [[rules]]
127
+ id = "basic-auth-header"
128
+ description = "Basic Auth Header"
129
+ regex = '''(?i)authorization\s*[:=]\s*['"]?(basic|bearer)\s+[a-zA-Z0-9+/=]{20,}['"]?'''
130
+ tags = ["auth", "header"]
131
+
132
+ [[rules]]
133
+ id = "authorization-header"
134
+ description = "Authorization Header"
135
+ regex = '''(?i)authorization\s*[:=]\s*['"]?[a-zA-Z0-9+/=]{20,}['"]?'''
136
+ tags = ["auth", "header"]
137
+
138
+ [[rules]]
139
+ id = "database-connection-string"
140
+ description = "Database Connection String"
141
+ regex = '''(?i)(mysql|postgres|postgresql|mongodb|redis|mssql)://[a-zA-Z0-9:@%/_\-?=&]+'''
142
+ tags = ["connection", "database"]
143
+
144
+ [[rules]]
145
+ id = "jwt-token"
146
+ description = "JWT Token"
147
+ regex = '''eyJ[A-Za-z0-9_=-]+\.eyJ[A-Za-z0-9_=-]+\.?[A-Za-z0-9_.+/=-]*'''
148
+ tags = ["token", "JWT"]
149
+
150
+ [[rules]]
151
+ id = "generic-api-token"
152
+ description = "Generic API Token"
153
+ regex = '''['"]token['"]\s*[:=]\s*['"]?[a-zA-Z0-9]{20,}['"]?'''
154
+ tags = ["key", "API", "token"]
155
+
156
+ [[rules]]
157
+ id = "connection-string-with-password"
158
+ description = "Connection String with Password"
159
+ regex = '''(?i)(password|passwd|pwd|secret)\s*[:=]\s*['"]?[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{}|;:,.<>?]{6,}['"]?'''
160
+ tags = ["credentials", "password"]
161
+
162
+ [rules.allowlist]
163
+ regexTarget = "line"
164
+ regexes = [
165
+ # Type annotations in dataclasses / Pydantic settings (colon, not equals)
166
+ '''(?i)(smtp|imap)[_-]?password\s*:\s*SecretStr''',
167
+ # Module-level test constant — value comes from os.environ, not a literal
168
+ '''_TEST_SECRET\s*=\s*SecretStr\(os\.environ''',
169
+ # Call sites that reference the constant (no string literal on the line)
170
+ '''(smtp|imap)_password\s*=\s*_TEST_SECRET''',
171
+ ]