AshCredo
Unofficial static code analysis checks for the Ash Framework, built as a Credo plugin.
AshCredo detects common anti-patterns, security pitfalls, and missing best practices in your Ash resources and domains by analysing unexpanded source AST.
[!WARNING] This project is experimental and might break frequently.
Installation
Add ash_credo to your list of dependencies in mix.exs:
def deps do
[
{:ash_credo, github: "leonqadirie/ash_credo", only: [:dev, :test], runtime: false}
]
endThen fetch the dependency:
mix deps.getSetup
With Igniter
If your project uses Igniter, you can set up AshCredo automatically:
mix ash_credo.install
This will create or update your .credo.exs to include the AshCredo plugin.
Manual
Register the plugin in your .credo.exs configuration:
%{
configs: [
%{
name: "default",
plugins: [{AshCredo, []}]
}
]
}That's it. All 18 checks are enabled by default. Run Credo as usual:
mix credo
To also include low-priority checks (such as MissingCodeInterface, ActionMissingDescription, and LargeResource), use strict mode:
mix credo --strictChecks
| Check | Category | Priority | Description |
|---|---|---|---|
AuthorizerWithoutPolicies | Warning | High |
Detects resources with Ash.Policy.Authorizer but no policies defined |
MissingChangeWrapper | Warning | High |
Flags builtin change functions (manage_relationship, set_attribute, ...) used without change wrapper in actions |
MissingPrimaryKey | Warning | High | Ensures resources with data layers have a primary key |
OverlyPermissivePolicy | Warning | High |
Flags unscoped authorize_if always() policies |
PinnedTimeInExpression | Warning | High |
Flags ^Date.utc_today() / ^DateTime.utc_now() in Ash expressions (frozen at compile time) |
SensitiveAttributeExposed | Warning | High |
Flags sensitive attributes (password, token, secret, ...) not marked sensitive?: true |
SensitiveFieldInAccept | Warning | High |
Flags privilege-escalation fields (is_admin, permissions, ...) in accept lists |
WildcardAcceptOnAction | Warning | High |
Detects accept :* on create/update actions (mass-assignment risk) |
EmptyDomain | Warning | Normal | Flags domains with no resources registered |
MissingDomain | Warning | Normal |
Ensures non-embedded resources set the domain: option |
NoActions | Warning | Normal | Flags resources with data layers but no actions defined |
MissingCodeInterface | Design | Low |
Suggests adding a code_interface for resources with actions |
MissingIdentity | Design | Normal |
Suggests identities for attributes like email, username, slug |
MissingPrimaryAction | Design | Normal |
Flags missing primary?: true when multiple actions of the same type exist |
MissingTimestamps | Design | Normal |
Suggests adding timestamps() to persisted resources |
ActionMissingDescription | Readability | Low |
Flags actions without a description |
BelongsToMissingAllowNil | Readability | Normal |
Flags belongs_to without explicit allow_nil? |
LargeResource | Refactor | Low | Flags resource files exceeding 400 lines |
Configuration
Checks are registered under the extra category. You can disable individual checks or customise their parameters in .credo.exs:
%{
configs: [
%{
name: "default",
plugins: [{AshCredo, []}],
checks: %{
extra: [
# Disable a check
{AshCredo.Check.Design.MissingCodeInterface, false},
# Set priority (can also be false to disable)
{AshCredo.Check.Warning.NoActions, [priority: :low]},
# Customise parameters
{AshCredo.Check.Refactor.LargeResource, [max_lines: 250]},
{AshCredo.Check.Warning.SensitiveAttributeExposed, [
sensitive_names: ~w(password token secret api_key)a
]},
{AshCredo.Check.Warning.SensitiveFieldInAccept, [
dangerous_fields: ~w(is_admin role permissions)a
]},
{AshCredo.Check.Design.MissingIdentity, [
identity_candidates: ~w(email username slug)a
]}
]
}
}
]
}Configurable parameters
The following checks accept custom parameters:
| Check | Parameter | Default | Description |
|---|---|---|---|
Design.MissingIdentity | identity_candidates | ~w(email username slug handle phone)a | Attribute names to suggest adding identities for |
Refactor.LargeResource | max_lines | 400 | Maximum line count before triggering |
Warning.SensitiveAttributeExposed | sensitive_names | ~w(password hashed_password password_hash token secret api_key private_key ssn)a |
Attribute names to flag when not marked sensitive?: true |
Warning.SensitiveFieldInAccept | dangerous_fields | ~w(is_admin admin permissions api_key secret_key)a |
Field names to flag when found in accept lists |
Contributing
- Fork the repository
-
Create your feature branch (
git switch -c my-new-check) -
Apply formatting and make sure tests and lints pass (
mix format,mix credo,mix test) - Commit your changes
- Open a pull request
License
MIT - see LICENSE for details.