
Research
/Security News
Critical Vulnerability in NestJS Devtools: Localhost RCE via Sandbox Escape
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
A static code analysis tool that detects dependency injection anti-patterns in Python projects. This linter helps enforce clean architecture principles by identifying direct instantiation or usage of project-specific dependencies within your code.
Dependency Injection is a design pattern where a class or function receives its dependencies from external sources rather than creating them internally. This pattern promotes:
This linter identifies cases where project-specific dependencies are directly created or used within functions or methods, rather than being injected as parameters. It helps enforce the principle that dependencies should be passed in, not created internally.
Additionally, the linter detects patch usage in test files, which is considered a bad practice as it can lead to brittle tests and make refactoring more difficult.
from project.user.repo import UserRepository
def process_data():
# BAD: Direct instantiation of project dependencies
repository = UserRepository() # DI001: Dependency injection
data = repository.get_all()
return data
from project.notifications import send_email
def send_notification():
# BAD: Direct usage of project module functions
send_email("user@example.com", "Hello") # DI001: Dependency injection
from project.db import context_manager
def backup_data():
# BAD: Using context managers from project modules
with context_manager(): # DI001: Dependency injection
# do something
pass
See more examples in my_module.py
import unittest
from unittest.mock import patch
class TestExample(unittest.TestCase):
# BAD: Using patch decorator
@patch('module.function') # Patch usage in tests
def test_function(self, mock_function):
mock_function.return_value = 'mocked'
self.assertEqual(mock_function(), 'mocked')
def test_function_with_context_manager(self):
# BAD: Using patch as context manager
with patch('module.function') as mock_function: # Patch usage in tests
mock_function.return_value = 'mocked'
self.assertEqual(mock_function(), 'mocked')
import pytest
def test_with_monkeypatch(monkeypatch):
# BAD: Using pytest monkeypatch
monkeypatch.setattr('module.function', lambda: 'mocked') # Patch usage in tests
assert 'mocked' == 'mocked'
# GOOD: Dependencies injected as parameters
def process_data(repository):
data = repository.get_all()
return data
# GOOD: Dependencies passed as arguments
def send_notification(email_sender):
email_sender("user@example.com", "Hello")
# GOOD: Context managers passed as parameters
def backup_data(context_manager):
with context_manager():
# do something
pass
pip install di-linter
di-linter path/to/project
di-linter --config-path di.toml
di-linter path/to/project --tests-path tests/unit tests/integration
flake8 --select=DI path/to/your/project
The configuration file di.toml
is optional.
If not provided, the linter will work with default settings.
# Required: The root directory of your project
project-root = "project"
# Optional: Objects to exclude from dependency injection checks
# Supports fnmatch pattern syntax with wildcards (*)
exclude-objects = [
"Settings", # Exact match
"DIContainer", # Exact match
"Config*", # All objects starting with Config
"*Repository", # All objects ending with Repository
"*Factory*" # All objects containing Factory
]
# Optional: Module patterns to exclude from dependency injection checks
# Supports fnmatch pattern syntax with wildcards (*)
exclude-modules = [
"project.endpoints", # Exact match
"project.api.*", # All modules in the api package
"*.endpoints", # All modules ending with endpoints
"project.*.models" # All models modules in any subpackage of project
]
# Optional: Paths to test directories or files for patch usage detection
tests-path = [
"tests", # Default test directory
"tests/unit", # Specific test subdirectory
"tests/integration" # Another test subdirectory
]
The linter looks for the configuration file in the following locations:
./di.toml
)You can also specify a custom path to the configuration file using the --config-path
option:
di-linter path/to/project --config-path /path/to/custom/di.toml
The project root is automatically detected by looking for marker files such as:
setup.py
setup.cfg
pyproject.toml
requirements.txt
Or by finding the directory where __init__.py
is no longer present in the parent directory.
The configuration file di.toml
is optional for the flake8 plugin as well.
If not provided, the plugin will work with default settings and follow
the same configuration file search logic as the standalone tool.
Add the following to your flake8 configuration file (e.g., .flake8
, setup.cfg
, or tox.ini
):
[flake8]
select = DI
di-exclude-objects = Settings,DIContainer
di-exclude-modules = project.endpoints,project.api.*,*.endpoints,project.*.models
di-tests-path = tests,tests/unit,tests/integration # Optional: paths to test directories for patch detection
di-config = path/to/di.toml # Optional: custom path to configuration file
You can also specify these options on the command line:
flake8 --select=DI --di-exclude-objects=Settings,DIContainer --di-exclude-modules=project.endpoints,project.api.* --di-tests-path=tests,tests/unit --di-config=path/to/di.toml path/to/your/project
The --di-config
option allows you to specify a custom path to the configuration file,
which is useful when you want to use a configuration file that's not in one of the default locations.
The linter supports excluding specific objects and modules from dependency injection checks using pattern matching. This is useful when you have certain objects or modules that you want to exempt from the dependency injection rules.
Both exclude-objects
and exclude-modules
support pattern matching using the fnmatch
syntax, which allows for flexible wildcard matching:
*
matches any sequence of characters (including none)"Settings"
- Matches exactly the object named "Settings""Config*"
- Matches all objects starting with "Config" (e.g., "Config", "ConfigLoader", "ConfigManager")"*Repository"
- Matches all objects ending with "Repository" (e.g., "UserRepository", "ProductRepository")"*Factory*"
- Matches all objects containing "Factory" (e.g., "Factory", "UserFactory", "FactoryMethod")"project.endpoints"
- Matches exactly the module named "project.endpoints""project.api.*"
- Matches all modules in the "project.api" package (e.g., "project.api.users", "project.api.products")"*.endpoints"
- Matches all modules ending with "endpoints" (e.g., "api.endpoints", "web.endpoints")"project.*.models"
- Matches all "models" modules in any subpackage of "project" (e.g., "project.users.models", "project.products.models")exclude-objects
with patterns matching the imported module name (e.g., "other_module*")exclude-modules
to exclude the entire file where the imports are usedYou can skip specific lines by adding a comment with # di: skip
:
def myfunc():
repository = UserRepository() # di: skip
Code | Description |
---|---|
DI001 | Dependency injection: Direct usage of project dependencies |
DI002 | Patch usage in tests: Using mocks or patches in test files |
DI Linter scanning modules:
Analyzing: /path/to/project
Analyzing tests in ['/path/to/tests']
/path/to/project/module.py:10: Dependency injection: UserRepository()
/path/to/project/module.py:15: Dependency injection: with db_transaction():
/path/to/tests/test_module.py:8: Patch usage in tests: @patch('module.function')
/path/to/tests/test_module.py:15: Patch usage in tests: with patch('module.function') as mock_function:
/path/to/tests/test_module.py:22: Patch usage in tests: monkeypatch.setattr('module.function', lambda: 'mocked')
┌─────────────────────────────────────────────── DI Linter ───────────────────────────────────────────────┐
│ Project path: /path/to/project │
│ Project root: project │
│ Exclude objects: [] │
│ Exclude modules: [] │
│ Tests paths: ['/path/to/tests'] │
│ Found 2 dependency injection problems and 3 patch usage problems! │
└────────────────────────────────────────────────────────────────────────────────────────────────────────┘
/path/to/project/module.py:10:5: DI001 Dependency injection: UserRepository()
/path/to/project/module.py:15:10: DI001 Dependency injection: with db_transaction():
/path/to/tests/test_module.py:8:5: DI002 Patch usage in tests: @patch('module.function')
/path/to/tests/test_module.py:15:10: DI002 Patch usage in tests: with patch('module.function') as mock_function:
/path/to/tests/test_module.py:22:5: DI002 Patch usage in tests: monkeypatch.setattr('module.function', lambda: 'mocked')
FAQs
Static code analysis for search of dependencies injection
We found that di-linter demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
Product
Customize license detection with Socket’s new license overlays: gain control, reduce noise, and handle edge cases with precision.
Product
Socket now supports Rust and Cargo, offering package search for all users and experimental SBOM generation for enterprise projects.