Skip to content

fix(serializers): prevent AttributeError and ObjectDoesNotExist in PluginConfigSerializer secret redaction. Closes #4052 - #4053

Open
harshitnub077 wants to merge 4 commits into
intelowlproject:developfrom
harshitnub077:fix/plugin-config-secret-redaction-null-owner
Open

harshitnub077 wants to merge 4 commits into
intelowlproject:developfrom
harshitnub077:fix/plugin-config-secret-redaction-null-owner

Conversation

@harshitnub077

@harshitnub077 harshitnub077 commented Oct 3, 2026 •

Copy link
Copy Markdown

Description

When serializing a PluginConfig with is_secret=True and for_organization=True, PluginConfigSerializer.CustomValueField.get_attribute directly evaluated instance.owner.pk and instance.owner.membership.organization.pk.

This led to unhandled 500 exceptions in two scenarios:

  1. AttributeError: 'NoneType' object has no attribute 'pk' when instance.owner is None (which is supported in the database constraints under api_app/models.py).
  2. ObjectDoesNotExist: User has no membership when instance.owner is set but lacks an active membership (e.g. users removed from an organization after the config was saved).
  3. Potential issues if request or an authenticated user is missing from serializer context.

This PR adds safe guards in PluginConfigSerializer.CustomValueField.get_attribute (api_app/serializers/plugin.py):

  • Checks user.is_authenticated and instance.owner is not None before comparing user.pk == instance.owner.pk.
  • Validates user.has_membership(), user.membership.is_admin, instance.owner.has_membership(), and compares organization_id directly using the FK column — avoiding any .membership attribute traversal that can raise RelatedObjectDoesNotExist.
  • Safely redacts secrets for unauthorized users and when owner / membership is absent.
  • Adds three unit tests in PluginConfigSerializerTestCase covering: global null-owner secrets, post-creation membership removal, and cross-organization visibility.

Note on test fixtures: The initial tests attempted to create PluginConfig(owner=None, for_organization=True) and PluginConfig(owner=user_without_membership, for_organization=True). Both are forbidden at the DB level by clean_for_organization() in api_app/interfaces.py. The tests were corrected to cover the real runtime edge cases: a valid owner=None global secret (exercises the null-owner guard without hitting the DB constraint), and a membership deleted after the config was saved (the actual scenario that triggered ObjectDoesNotExist).

Closes #4052

Type of change

  • Bug fix (non-breaking change which fixes an issue).

Checklist

  • I have read and understood the rules about how to Contribute to this project
  • The pull request is for the branch develop
  • A new plugin (analyzer, connector, visualizer, playbook, pivot or ingestor) was added or changed, in which case:
    • I strictly followed the documentation "How to create a Plugin"
    • Usage file was updated. A link to the PR to the docs repo has been added as a comment here.
    • Advanced-Usage was updated (in case the plugin provides additional optional configuration). A link to the PR to the docs repo has been added as a comment here.
    • I have dumped the configuration from Django Admin using the dumpplugin command and added it in the project as a data migration. ("How to share a plugin with the community")
    • If a File analyzer was added and it supports a mimetype which is not already supported, you added a sample of that type inside the archive test_files.zip and you added the default tests for that mimetype in test_classes.py.
    • If you created a new analyzer and it is free (does not require any API key), please add it in the FREE_TO_USE_ANALYZERS playbook by following this guide.
    • Check if it could make sense to add that analyzer/connector to other freely available playbooks.
    • I have provided the resulting raw JSON of a finished analysis and a screenshot of the results.
    • If the plugin interacts with an external service, I have created an attribute called precisely url that contains this information. This is required for Health Checks (HEAD HTTP requests).
    • If a new analyzer has beed added, I have created a unittest for it in the appropriate dir. I have also mocked all the external calls, so that no real calls are being made while testing.
    • I have added that raw JSON sample to the get_mocker_response() method of the unittest class. This serves us to provide a valid sample for testing.
    • I have created the corresponding DataModel for the new analyzer following the documentation
  • I have inserted the copyright banner at the start of the file: # This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl # See the file 'LICENSE' for copying permission.
  • Please avoid adding new libraries as requirements whenever it is possible. Use new libraries only if strictly needed to solve the issue you are working for. In case of doubt, ask a maintainer permission to use a specific library.
  • If external libraries/packages with restrictive licenses were added, they were added in the Legal Notice section.
  • Linters (Ruff) gave 0 errors. If you have correctly installed pre-commit, it does these checks and adjustments on your behalf.
  • I have added tests for the feature/bug I solved (see tests folder). All the tests (new and old ones) gave 0 errors.
  • If the GUI has been modified:
    • I have a provided a screenshot of the result in the PR.
    • I have created new frontend tests for the new component or updated existing ones.
  • After you had submitted the PR, if DeepSource, Django Doctors or other third-party linters have triggered any alerts during the CI checks, I have solved those alerts.
  • I have addressed raised Copilot issues. In case of FPs, I have commented the Copilot issue and proved that it is wrong before having the comment resolved.
  • I have reviewed and verified any LLM-generated code included in this PR. Also, I have explicitly stated that I have used LLMs in this PR.

Note on LLM assistance: An LLM was used to assist with static code analysis and drafting tests. All changes have been manually reviewed and verified against repository standards.

…uginConfigSerializer secret redaction. Closes intelowlproject#4052

Signed-off-by: Harshit Kudhial <harshitkudhial@gmail.com>
Copilot AI balanced review requested due to automatic review settings October 3, 2026 11:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…erializerTestCase

The two new tests created DB states that clean_for_organization() forbids:
- owner=None + for_organization=True raises ValidationError at PluginConfig.clean()
- for_organization=True with an owner who has no membership also raises ValidationError

Fix:
- test_secret_no_owner_global_config: tests a valid global secret (owner=None,
  for_organization=False) to exercise the null-owner guard in get_attribute without
  hitting the DB constraint.
- test_secret_redaction_owner_membership_removed_after_creation: creates the
  owner's membership first so the object saves, then deletes the membership to
  simulate a post-creation removal — the real scenario that previously raised
  ObjectDoesNotExist.
Copilot AI balanced review requested due to automatic review settings October 3, 2026 11:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI balanced review requested due to automatic review settings October 7, 2026 08:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…organization on configs with membership-less owners
Copilot AI balanced review requested due to automatic review settings October 7, 2026 16:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants