Skip to content

feat(spp_api_v2): add outgoing API log model, service, and auditor security#38

Open
jeremi wants to merge 4 commits into19.0from
feat/api-outgoing-log
Open

feat(spp_api_v2): add outgoing API log model, service, and auditor security#38
jeremi wants to merge 4 commits into19.0from
feat/api-outgoing-log

Conversation

@jeremi
Copy link
Member

@jeremi jeremi commented Feb 17, 2026

Summary

  • Add spp.api.outgoing.log model to track outgoing API requests (URL, method, headers, payload, response, status, duration)
  • Add OutgoingApiLogService for creating and querying log entries with automatic payload masking
  • Add auditor security group with read-only access to API log payloads (request/response bodies)
  • Add tree/form views and menu entries under API Configuration
  • Add comprehensive tests for model, service, and security

Test plan

  • Run test_single_module.sh spp_api_v2
  • Verify outgoing log menu appears under API Configuration
  • Verify auditor group can see payload fields, regular users cannot
  • Verify log entries are created with correct masking of sensitive fields

Note

Medium Risk
Introduces new persistence of external request/response metadata and expands audit/security rules; mistakes in masking or field-group restrictions could leak sensitive data, though changes are well-tested and scoped to logging/UI.

Overview
Adds a new spp.api.outgoing.log model + menu/views to record outgoing HTTP calls (URL/endpoint/method, request/response summaries, status codes, duration, origin context, errors) and expose them in the API V2 configuration UI.

Introduces OutgoingApiLogService to write these logs safely via sudo(), while masking common secret/token fields, sanitizing sensitive query params in URLs, and truncating oversized payloads; failures to log are swallowed to avoid breaking the caller.

Tightens log data exposure by adding an opt-in group_api_v2_auditor/privilege_api_v2_auditor and gating sensitive fields on both the existing spp.api.audit.log (IP/user-agent/search params/fields returned/error detail) and the new outgoing log model, with new access rules and tests covering field-level AccessError behavior.

Written by Cursor Bugbot for commit ff1b282. This will update automatically on new commits. Configure here.

@gemini-code-assist
Copy link

Summary of Changes

Hello @jeremi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the API auditing capabilities by introducing a new system for logging outgoing API calls. It provides a detailed record of external interactions, crucial for troubleshooting, compliance, and security analysis. The changes also include a specialized auditor role with restricted access to sensitive log data, ensuring that only authorized personnel can view detailed request and response payloads. This feature set improves the overall observability and security posture of the API V2 module.

Highlights

  • New Outgoing API Log Model: Introduced a new Odoo model, spp.api.outgoing.log, to comprehensively track all outgoing API requests, capturing details such as URL, HTTP method, request/response payloads, status, duration, and originating context.
  • Dedicated Outgoing API Log Service: Developed OutgoingApiLogService to provide a robust and fault-tolerant mechanism for logging outgoing API calls. This service includes automatic payload truncation for large data and gracefully handles logging failures to prevent disruption of core operations.
  • Auditor Security Group for Sensitive Data: Implemented a new API V2: Auditor security group. Users assigned to this group gain read-only access to sensitive information within both incoming API audit logs and the newly introduced outgoing API logs, including request/response bodies, IP addresses, and error details, while regular users remain restricted.
  • User Interface and Menu Integration: Added dedicated tree, form, and search views for the outgoing API logs, along with a new menu entry under 'API V2' configuration, making these logs easily accessible within the Odoo interface.
  • Comprehensive Testing: Included extensive unit tests for the new outgoing API log model, the logging service, and the auditor security group, ensuring the reliability and correct enforcement of access controls for sensitive data.
Changelog
  • spp_api_v2/manifest.py
    • Registered the new outgoing API log views.
  • spp_api_v2/models/init.py
    • Integrated the new api_outgoing_log model.
  • spp_api_v2/models/api_audit_log.py
    • Restricted access to sensitive fields (IP address, user agent, search parameters, returned fields/extensions, error detail) in the ApiAuditLog model to the new group_api_v2_auditor.
  • spp_api_v2/models/api_outgoing_log.py
    • Added the spp.api.outgoing.log model to track outgoing API requests.
    • Defined fields for URL, endpoint, HTTP method, request/response summaries, status code, user, origin model/record, timestamp, duration, service name/code, status, and error detail.
    • Applied auditor group security to request_summary, response_summary, and error_detail fields.
    • Implemented a computed display_name field for better record identification.
    • Provided a log_call method for creating new outgoing log entries.
  • spp_api_v2/security/groups.xml
    • Defined a new security group group_api_v2_auditor for API V2 auditors.
    • Configured the group_api_v2_auditor to imply group_api_v2_viewer for menu access.
    • Linked the group_api_v2_auditor to the spp_security.group_spp_admin.
  • spp_api_v2/security/ir.model.access.csv
    • Configured access control for the new spp.api.outgoing.log model for viewer, officer, and manager groups.
  • spp_api_v2/security/privileges.xml
    • Introduced a new privilege privilege_api_v2_auditor for viewing sensitive payload data in API logs.
  • spp_api_v2/services/init.py
    • Integrated the new outgoing_api_log_service.
  • spp_api_v2/services/outgoing_api_log_service.py
    • Added the OutgoingApiLogService class for logging outgoing API calls.
    • Implemented a log_call method that wraps the model's logging, ensuring logging failures do not halt API calls.
    • Included a _truncate_payload utility to limit the size of stored request/response payloads.
  • spp_api_v2/tests/init.py
    • Registered new test files for the outgoing API log model and service.
  • spp_api_v2/tests/test_api_audit_log.py
    • Added imports for Command and AccessError.
    • Introduced TestAuditLogAuditorSecurity to verify field-level security for the auditor group on existing API audit log fields.
  • spp_api_v2/tests/test_api_outgoing_log.py
    • Added TestApiOutgoingLog to test the functionality of the new spp.api.outgoing.log model.
    • Included TestOutgoingLogAuditorSecurity to verify field-level security for the auditor group on the new outgoing API log fields.
  • spp_api_v2/tests/test_outgoing_api_log_service.py
    • Implemented TestOutgoingApiLogService to test the new outgoing API log service, covering logging, error handling, and payload truncation.
  • spp_api_v2/views/api_outgoing_log_views.xml
    • Created tree, form, and search views for the spp.api.outgoing.log model.
    • Applied auditor group security to sensitive fields (request_summary, response_summary, error_detail) within the form view.
    • Defined an action action_api_outgoing_log to display the outgoing API logs.
  • spp_api_v2/views/menu.xml
    • Added a new menu item 'Outgoing API Logs' under the 'API V2' configuration, accessible to viewers.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive feature for logging outgoing API calls, including a new model, service, views, and security configurations, and adds a new 'Auditor' role to control access to sensitive log data. While the implementation is robust, well-structured, and thoroughly tested, and the code is clean, there are two medium-severity security concerns related to insecure data handling. The promised 'automatic payload masking' is missing from the service implementation, leading to the potential logging of unmasked secrets. Additionally, the full URL of outgoing calls is logged and exposed to low-privileged users, which could leak sensitive information if secrets are passed in query parameters. Addressing these security issues is crucial to prevent data leakage from the logging system itself. There is also one minor suggestion for improving code style in a new test file.

Comment on lines 80 to 81

Choose a reason for hiding this comment

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

security-medium medium

The OutgoingApiLogService is missing the 'automatic payload masking' functionality mentioned in the pull request description. The current implementation only performs truncation of large payloads via _truncate_payload, but it does not redact sensitive information such as credentials, tokens, or PII from the request and response summaries. Storing unmasked sensitive data in the database, even if restricted to auditors, poses a security risk. Please implement a masking mechanism to redact sensitive keys (e.g., 'Authorization', 'password', 'token') before logging.

Comment on lines 27 to 31
url = fields.Char(
required=True,
index=True,
help="Full URL called",
)

Choose a reason for hiding this comment

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

security-medium medium

The url field stores the full URL of outgoing API calls, which may contain sensitive information such as API keys or tokens in query parameters. Unlike the payload fields, the url field is not restricted to the group_api_v2_auditor group, making it visible to any user with 'viewer' access to the logs. Consider adding groups="spp_api_v2.group_api_v2_auditor" to this field or implementing logic to strip sensitive query parameters before logging.

Choose a reason for hiding this comment

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

medium

For better code style and to avoid repeated imports, import json should be moved to the top of the file with other imports. This also applies to the import on line 176.

@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

❌ Patch coverage is 98.78049% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.87%. Comparing base (5ac7496) to head (6438991).

Files with missing lines Patch % Lines
spp_api_v2/services/outgoing_api_log_service.py 93.65% 4 Missing ⚠️
spp_api_v2/tests/test_outgoing_api_log_service.py 98.83% 2 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (5ac7496) and HEAD (6438991). Click for more details.

HEAD has 3 uploads less than BASE
Flag BASE (5ac7496) HEAD (6438991)
fastapi 1 0
endpoint_route_handler 1 0
spp_alerts 1 0
Additional details and impacted files
@@            Coverage Diff             @@
##             19.0      #38      +/-   ##
==========================================
- Coverage   71.31%   61.87%   -9.45%     
==========================================
  Files         299      388      +89     
  Lines       23618    31578    +7960     
==========================================
+ Hits        16844    19538    +2694     
- Misses       6774    12040    +5266     
Flag Coverage Δ
endpoint_route_handler ?
fastapi ?
spp_alerts ?
spp_api_v2 89.58% <98.78%> (+0.44%) ⬆️
spp_api_v2_change_request 66.61% <ø> (ø)
spp_api_v2_cycles 65.45% <ø> (ø)
spp_api_v2_data 48.67% <ø> (ø)
spp_api_v2_entitlements 68.43% <ø> (?)
spp_api_v2_products 64.39% <ø> (?)
spp_api_v2_service_points 63.12% <ø> (?)
spp_api_v2_vocabulary 43.70% <ø> (?)
spp_base_common 92.81% <ø> (ø)
spp_dci_client_dr 75.33% <ø> (?)
spp_dci_client_ibr 78.05% <ø> (?)
spp_dci_server 51.27% <ø> (?)
spp_mis_demo_v2 0.00% <ø> (?)
spp_programs 49.56% <ø> (ø)
spp_security 51.08% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

def _compute_display_name(self):
for record in self:
timestamp_str = record.timestamp.strftime("%Y-%m-%d %H:%M") if record.timestamp else ""
record.display_name = f"{record.http_method} {record.endpoint or 'API Call'} @ {timestamp_str}"
Copy link

Choose a reason for hiding this comment

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

Display name fallback uses literal string, not URL

Medium Severity

_compute_display_name falls back to the literal string 'API Call' when endpoint is empty, but the test test_display_name_falls_back_to_url asserts that the URL appears in display_name when endpoint is not set. The computed expression record.endpoint or 'API Call' never references record.url, and url is also missing from the @api.depends decorator. This mismatch means the test will fail, and the intended behavior of showing the URL as a fallback identifier is not implemented.

Additional Locations (1)

Fix in Cursor Fix in Web

Restrict sensitive PII fields in API logs to a dedicated
group_api_v2_auditor group. Regular viewers can see log metadata
(timestamp, endpoint, status, duration) but not payloads or error
details.

- Add privilege_api_v2_auditor and group_api_v2_auditor
- Auditor implies Viewer; Admin implies Auditor
- Restrict outgoing log: request_summary, response_summary, error_detail
- Restrict audit log: search_parameters, fields_returned,
  extensions_returned, ip_address, user_agent, error_detail
- Hide payload pages and error_detail in outgoing log form view
- Add field-level security tests for both models
Wire up the spp.api.outgoing.log model with manifest registration,
ACL rules (viewer read-only, officer/manager read+write), the
OutgoingApiLogService wrapper with payload truncation and error
resilience, and an "Outgoing API Logs" menu item under API V2.
… API log

- Add groups restriction to url field and XML view to limit access to auditors
- Add _sanitize_url method to strip sensitive query parameters before logging
- Call _sanitize_url in log_call so stored URLs never contain tokens or keys
- Add tests covering _sanitize_url and end-to-end URL sanitization in log_call
…urity leak

The _compute_display_name method was falling back to record.url when
endpoint was not set. Since url has groups="spp_api_v2.group_api_v2_auditor"
but display_name is store=True with no groups restriction, the URL value
was being persisted into an unrestricted field, bypassing field-level security.
Also adds url to @api.depends implicitly by removing the reference entirely.
Replace the url fallback with a generic "API Call" string.
@jeremi jeremi force-pushed the feat/api-outgoing-log branch from ff1b282 to 6438991 Compare February 18, 2026 14:18
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.


self.assertTrue(log.display_name)
self.assertIn("GET", log.display_name)
self.assertIn("https://example.org/api/test", log.display_name)
Copy link

Choose a reason for hiding this comment

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

Test expects URL in display_name but model uses literal fallback

Medium Severity

test_display_name_falls_back_to_url asserts the URL appears in display_name, but _compute_display_name falls back to the literal string 'API Call' when endpoint is not set — not the url field. This test will always fail. Since url is restricted via groups="spp_api_v2.group_api_v2_auditor", using it in the stored display_name would leak sensitive data to non-auditors, so the model fallback is likely correct and the test assertion is wrong.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Contributor

@emjay0921 emjay0921 left a comment

Choose a reason for hiding this comment

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

Reviewed code and tested manually on a running instance. Architecture is clean — model stores, service sanitizes (masking, URL sanitization, truncation). Auditor group field-level security verified working via XML-RPC.

Minor items for follow-up:

  1. Model log_call docstring says request_summary: Request payload (secrets redacted) but the model doesn't redact — only the service does. Misleading docstring.
  2. error_detail is not sanitized by the service layer — could leak internal infrastructure details (IPs, hostnames). The field is gated by auditor group in the UI, but consider sanitizing at the service level too.

Neither is a blocker. Good to merge.

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.

3 participants

Comments