Skip to content

Commit 9694d05

Browse files
fix: reject required custom auth fields (#44)
1 parent b646afa commit 9694d05

9 files changed

Lines changed: 99 additions & 12 deletions

File tree

.github/workflows/self-test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ jobs:
5454
fi
5555
echo "✅ Auto-discovery correctly skips tests/"
5656
57+
- name: Test validate_integration.py — focused pytest regressions
58+
run: pytest tests/test_validate_integration.py
59+
5760
- name: Test check_imports.py — valid imports
5861
run: |
5962
python scripts/check_imports.py tests/examples/submodule-imports/valid_submodules.py

scripts/docs/validate_integration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Validates based on `auth.type`:
107107
| Auth Type | Required Fields | Description |
108108
|-----------|----------------|-------------|
109109
| `platform` | `provider`, `scopes` (array) | OAuth2-based authentication |
110-
| `custom` | `fields.properties` | API key or token-based authentication |
110+
| `custom` | `fields.properties` | API key or token-based authentication. `auth.fields.required` must be absent or empty; credential presence is handled by the platform connection flow. |
111111
| _(omitted)_ || No authentication (public APIs) |
112112

113113
#### Action Configuration (`_validate_actions_config`)

scripts/validate_integration.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,10 @@ def _check_required_files(self):
121121
# use absolute imports like 'from <integration> import <instance>'.
122122
has_actions_dir = (self.path / 'actions').is_dir()
123123
if not (self.path / '__init__.py').exists() and not has_actions_dir:
124-
self.add_warning("Missing __init__.py (required for package-style integrations, optional for modular integrations with actions/)")
124+
self.add_warning(
125+
"Missing __init__.py "
126+
"(required for package-style integrations, optional for modular integrations with actions/)"
127+
)
125128

126129
# Check for forbidden files
127130
if (self.path / 'integration.py').exists():
@@ -244,6 +247,14 @@ def _validate_auth_config(self):
244247
self.add_error("Custom auth requires 'fields' configuration")
245248
elif 'properties' not in auth.get('fields', {}):
246249
self.add_error("Custom auth fields must have 'properties' defined")
250+
else:
251+
required_fields = auth['fields'].get('required')
252+
if required_fields:
253+
self.add_error(
254+
"Custom auth fields must not define a non-empty auth.fields.required array. "
255+
"Credential collection and presence are handled by the platform connection flow; "
256+
"define auth.fields.properties only."
257+
)
247258

248259
elif auth_type is not None:
249260
self.add_warning(f"Unknown auth type: '{auth_type}'. Expected 'platform' or 'custom'")

tests/examples/bad-icon/config.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
"label": "API Key",
1616
"help_text": "Enter your API key"
1717
}
18-
},
19-
"required": ["api_key"]
18+
}
2019
}
2120
},
2221
"actions": {

tests/examples/good-integration/config.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
"label": "API Key",
1616
"help_text": "Enter your API key"
1717
}
18-
},
19-
"required": ["api_key"]
18+
}
2019
}
2120
},
2221
"actions": {

tests/examples/long-lines-integration/config.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
"label": "API Key",
1616
"help_text": "Enter your API key"
1717
}
18-
},
19-
"required": ["api_key"]
18+
}
2019
}
2120
},
2221
"actions": {

tests/examples/modular-integration/config.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
"label": "API Key",
1616
"help_text": "Enter your API key"
1717
}
18-
},
19-
"required": ["api_key"]
18+
}
2019
}
2120
},
2221
"actions": {

tests/sample-api/config.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
"label": "API Key",
1616
"help_text": "Enter your API key from the dashboard"
1717
}
18-
},
19-
"required": ["api_key"]
18+
}
2019
}
2120
},
2221
"actions": {

tests/test_validate_integration.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import sys
2+
from pathlib import Path
3+
4+
5+
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
6+
7+
from validate_integration import IntegrationValidator # noqa: E402
8+
9+
10+
def _validate_auth(config: dict) -> IntegrationValidator:
11+
validator = IntegrationValidator(Path("custom-auth"))
12+
validator.config = config
13+
validator._validate_auth_config()
14+
return validator
15+
16+
17+
def test_custom_auth_without_required_is_valid() -> None:
18+
validator = _validate_auth(
19+
{
20+
"auth": {
21+
"type": "custom",
22+
"fields": {
23+
"type": "object",
24+
"properties": {
25+
"api_key": {"type": "string"},
26+
},
27+
},
28+
}
29+
}
30+
)
31+
32+
assert validator.errors == []
33+
assert validator.warnings == []
34+
35+
36+
def test_custom_auth_empty_required_array_is_allowed() -> None:
37+
validator = _validate_auth(
38+
{
39+
"auth": {
40+
"type": "custom",
41+
"fields": {
42+
"type": "object",
43+
"properties": {
44+
"api_key": {"type": "string"},
45+
},
46+
"required": [],
47+
},
48+
}
49+
}
50+
)
51+
52+
assert validator.errors == []
53+
assert validator.warnings == []
54+
55+
56+
def test_custom_auth_non_empty_required_array_is_rejected() -> None:
57+
validator = _validate_auth(
58+
{
59+
"auth": {
60+
"type": "custom",
61+
"fields": {
62+
"type": "object",
63+
"properties": {
64+
"api_key": {"type": "string"},
65+
},
66+
"required": ["api_key"],
67+
},
68+
}
69+
}
70+
)
71+
72+
messages = [error.message for error in validator.errors]
73+
assert messages == [
74+
"Custom auth fields must not define a non-empty auth.fields.required array. "
75+
"Credential collection and presence are handled by the platform connection flow; "
76+
"define auth.fields.properties only."
77+
]
78+
assert validator.warnings == []

0 commit comments

Comments
 (0)