Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 26, 2025

  • Implement caching infrastructure reuse (FcliRecordsCache in _common package)
  • Add async execution methods: fcli.executeAsync, fcli.getPage, fcli.cancelCollection, fcli.clearCache
  • Add product-specific session methods (extensible architecture)
    • fcli.ssc.login / fcli.ssc.logout
    • fcli.fod.login / fcli.fod.logout
  • Remove paging from synchronous fcli.execute (use async for large datasets)
  • Auto-logout sessions on RPC server shutdown
  • Auto-add session options to commands
  • Update documentation with new methods
  • Add unit tests for session and async methods
  • Run code review and address feedback
  • Run CodeQL security check (no issues found)

Summary

This PR implements a JSON-RPC 2.0 server with the following features:

Session Methods (per product, extensible)

Method Description
fcli.ssc.login Login to SSC (url + user/password or token, with optional client-auth-token and sc-sast-url)
fcli.ssc.logout Logout from SSC
fcli.fod.login Login to FoD (url + client-id/secret or user/password/tenant)
fcli.fod.logout Logout from FoD

SSC Login Parameters

  • url (required): SSC URL
  • user + password: User credentials auth
  • token: UnifiedLoginToken or CIToken
  • client-auth-token (optional): SC-SAST client auth token for SC-SAST authentication
  • sc-sast-url (optional): SC-SAST controller URL
  • expire-in (optional): Token expiration (e.g., "1d", "8h")
  • insecure (optional): Allow insecure SSL connections

FoD Login Parameters

  • url (required): FoD URL
  • client-id + client-secret: API client credentials auth
  • user + password + tenant: User credentials auth
  • insecure (optional): Allow insecure SSL connections

Sessions are automatically logged out when the RPC server terminates. The --ssc-session or --fod-session options are automatically added to commands.

Execution Methods

Method Description
fcli.execute Synchronous execution, returns ALL results (no paging)
fcli.executeAsync Start async execution, returns cacheKey
fcli.getPage Get page of results from cache
fcli.cancelCollection Cancel in-progress collection
fcli.clearCache Clear cache entries

Info Methods

Method Description
fcli.listCommands List available fcli commands
fcli.version Get version information
rpc.listMethods Discover available RPC methods

Architecture Notes

  • Product session handlers are registered via RpcSessionManager - new products can be added by implementing login/logout handlers
  • SC-SAST and SC-DAST share the SSC session type
  • Cache uses FcliRecordsCache with option resolver for automatic session injection
Original prompt

please generate a pr that wraps FcliCommandExecutor in a json-rpc server that can be started throught 'fcli util rpc-server start' command, similar to 'fcli util mcp-server start'. Primary use case is for integrating fcli into IDE plugins for accessing fortify products through fcli, for example to report security issues, running arbitrary fcli commands or general rest calls to the various products (either through dedicated function or 'fcli * rest call' commands), also allowing for dynamic discovery of fcli commands (through dedicated function or 'fcli util all-commands list). Use either existing json-rpc library (with minimal dependencies) or custom implementation (if easy and little maintenance required). please consider compatibility with graalvm native binaries). where possible, re-use existing mcp-server code, even if some refactoring is required. For commands that support it, records should be returned in streaming mode or paged. IDE plugins should mostly be just minimal wrappers around fcli json-rpc invocations, so things like caching should be handled by fcli, with potentially ide plugin configuring cache characteristics (size, lifetime, ...). please consider copilot instructions from feat/ci-updates branch). in the pr, please document decisions made, decisions yet to be taken, suggestions for future improvements, etcetera. please let me know if you need any further info.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits November 26, 2025 05:25
Implements a lightweight JSON-RPC 2.0 server accessible via 'fcli util rpc-server start'.
This provides programmatic access to fcli functionality for IDE plugins.

Key features:
- Custom JSON-RPC 2.0 implementation (no external dependencies, GraalVM compatible)
- RPC methods: fcli.execute, fcli.listCommands, fcli.version, rpc.listMethods
- Pagination support for record-producing commands
- Structured JSON responses for IDE integration

Reuses patterns from MCP server implementation.

Co-authored-by: rsenden <[email protected]>
- Remove unused ExecutorService and thread pool parameter from JsonRpcServer
- Add validation for limit (must be > 0) and offset (must be >= 0) parameters
- Cache qualifiedName in local variable in filter lambda
- Use JsonRpcError.INTERNAL_ERROR constant in fallback error response

Co-authored-by: rsenden <[email protected]>
Copilot AI changed the title [WIP] Add JSON-RPC server for FcliCommandExecutor Add JSON-RPC server for IDE plugin integration Nov 26, 2025
Copilot AI requested a review from rsenden November 26, 2025 05:37
@rsenden
Copy link
Contributor

rsenden commented Nov 26, 2025

@copilot Some fcli commands that return records may take a long time to complete, for example 'issue list' commands may load thousands of records, but we need to be able to return a paged response as soon as a given page has been loaded. Also, fcli commands always load all pages; obviously we don't want to repeat that every time the execute method is invoked with same arguments. Has this been catered for? In the MCP server, we solved this by background loading and caching of all data, and returning each requested page as soon as the appropriate records have been loaded. However, caching comes with its own challenges (cache size, lifetime, ...), so maybe you can propose better solutions? It would be great if we can add generic paging options to all fcli commands that extend from AbstractOutputCommand and integrate this with the product-specific paging helpers, as that could also be useful for regular fcli command invocations, like allowing users to just get the first couple of record to see record structure/contents. But how would we properly do this in combination with client-side filtering based on the --query option? I.e., suppose the user requests start=50 & limit=25, and all of those records get filtered by the client-side query; should we just translate this to server-side start & limit parameters and return an empty page? We can't just load additional server-side pages to look for 25 matching records, as then on the next request with start=75, we don't know the appropriate server-side start position. Any suggestions? As for caching, we could use an in-memory cache, or possibly integrate with standard fcli --store functionality to store filtered results on disk, and then load further pages from stored contents. But then we need file cleanup, either automatically (some timeout or once client has requested last page) or by requiring the rpc caller to notify us when they're done loading pages.

Copy link
Contributor Author

Copilot AI commented Nov 26, 2025

@copilot Some fcli commands that return records may take a long time to complete, for example 'issue list' commands may load thousands of records, but we need to be able to return a paged response as ...

The current implementation does not have background loading/caching - it loads all records on each request, which is indeed inefficient for large datasets.

Proposed approach for RPC server:

  1. Reuse MCP caching infrastructure: The MCPToolFcliRecordsCache can be adapted for RPC. Add a cacheKey parameter (or generate from command+args) so clients can reference cached results across requests.

  2. New RPC methods for explicit cache management:

    • fcli.executeAsync - starts background collection, returns cacheKey immediately
    • fcli.getPage - retrieves page from cache by cacheKey, waits if needed
    • fcli.cancelCollection - cancels ongoing collection
    • fcli.clearCache - explicit cache cleanup
  3. Regarding --query + pagination: This is a fundamental challenge since client-side filtering happens after loading. Two options:

    • Option A: Load all, cache filtered results. Client specifies query upfront in fcli.executeAsync, and cached result is the filtered set.
    • Option B: Return unfiltered pages with metadata. Let IDE plugin apply filtering client-side (simpler but shifts work to plugin).
  4. For generic fcli paging: Extending AbstractOutputCommand with native --start/--limit that integrates with product paging helpers is valuable but separate from RPC caching. It would help both regular CLI users and RPC consumers, but requires careful design for the --query interaction you described.

Should I implement the caching infrastructure reuse (option 1+2), or would you prefer a different approach? The --query interaction needs further discussion to determine the right tradeoff.

@rsenden
Copy link
Contributor

rsenden commented Nov 26, 2025

@copilot Please implement 1+2, and also look for other mcp server code that makes sense to be reused for json-rpc server, taking into account the different user cases (for example, rpc clients can directly pass proper --query option, whereas mcp server adds some sugar coating to make it easier for LLMs to see what field can be queried upon (but only covers small subset of queryable fields, which is not what we want for rpc server). All shared code should be moved to an _common package or similar, just like in some other fcli modules.

- Create shared _common/helper package with reusable components:
  - FcliRecordsCache: Background loading and caching for large result sets
  - FcliToolResult: Unified result class for fcli command execution
  - FcliRunnerHelper: Helper methods for running fcli commands

- Add new RPC methods for async execution and cache management:
  - fcli.executeAsync: Start background collection, returns cacheKey
  - fcli.getPage: Retrieve page from cache by cacheKey with wait option
  - fcli.cancelCollection: Cancel in-progress collection
  - fcli.clearCache: Clear cache entries

- Update documentation and tests for new methods

Co-authored-by: rsenden <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 26, 2025

@copilot Please implement 1+2, and also look for other mcp server code that makes sense to be reused for json-rpc server, taking into account the different user cases (for example, rpc clients can dir...

Implemented in commit 0c633c0:

  1. Created shared _common/helper package with reusable components:

    • FcliRecordsCache: Background loading with LRU caching, TTL-based expiration
    • FcliToolResult: Unified result class for fcli command execution
    • FcliRunnerHelper: Common command execution helpers
  2. Added new RPC methods:

    • fcli.executeAsync - starts background collection, returns cacheKey
    • fcli.getPage - retrieves page from cache (with optional wait for completion)
    • fcli.cancelCollection - cancels in-progress collection
    • fcli.clearCache - clears cache entries

Note: I intentionally did NOT reuse the MCP-specific sugar coating for queries (like MCPToolArgHandlers) since RPC clients can pass proper --query options directly. The shared code focuses on the core caching/execution infrastructure that benefits both servers.

@rsenden
Copy link
Contributor

rsenden commented Nov 26, 2025

@copilot Is it possible to rename the branch for this PR to feat/json-rpc-server? I'd also like to publish a development release; this requires pushing a tag that matches PR branch name, something like 'dev_', like dev_feat.json-rpc-server; ci.yml workflow file uses following bash code to determine tag name to check for: branch="${GITHUB_REF#refs/heads/}"
tag="dev_${branch//[^a-zA-Z0-9_.-]/.}" # Replace all special characters by a dot
tag="${tag//dev_dev./dev_}" # Remove duplicate dev-prefix
tag="${tag//dev_rel./dev_}" # Remove rel-prefix
Then, if necessary, trigger the ci workflow to run to publish dev release.

Copy link
Contributor Author

Copilot AI commented Nov 26, 2025

@copilot Is it possible to rename the branch for this PR to feat/json-rpc-server? I'd also like to publish a development release; this requires pushing a tag that matches PR branch name, something lik...

Unfortunately, I cannot rename branches directly - this is outside my capabilities in the sandboxed environment. You would need to rename the branch yourself using:

# Locally rename and push
git checkout copilot/add-json-rpc-server-for-fcli
git branch -m feat/json-rpc-server
git push origin -u feat/json-rpc-server
git push origin --delete copilot/add-json-rpc-server-for-fcli

Or use GitHub's UI to create a new PR from a renamed branch. After renaming, you can create the development release tag matching the new branch name.

@rsenden
Copy link
Contributor

rsenden commented Nov 26, 2025

@copilot Based on the previous comments, please create the proper tag for current branch name then, and trigger ci workflow to build dev release

Copy link
Contributor Author

Copilot AI commented Nov 26, 2025

@copilot Based on the previous comments, please create the proper tag for current branch name then, and trigger ci workflow to build dev release

Unfortunately, I cannot create or push tags to GitHub - this is outside my sandboxed environment capabilities. I can only commit code changes through the report_progress tool.

To create the dev release tag yourself:

# Get current branch name
git branch --show-current
# Output: copilot/add-json-rpc-server-for-fcli

# Create and push tag matching the branch pattern
git tag dev_copilot/add-json-rpc-server-for-fcli
git push origin dev_copilot/add-json-rpc-server-for-fcli

This should trigger the CI workflow to build the dev release. Alternatively, you could manually trigger the workflow from the GitHub Actions UI if the repository has a workflow_dispatch trigger configured.

@fransvanbuul fransvanbuul marked this pull request as ready for review November 26, 2025 12:21
@fransvanbuul fransvanbuul marked this pull request as draft November 26, 2025 12:36
@rsenden
Copy link
Contributor

rsenden commented Nov 26, 2025

@copilot Given 'fcli util rpc-server start' usage help, some potential improvements. The synchronous execute method shouldn't support paging; clients must use async execute method. Also, contrary to mcp server, session login commands can be run through the execute method, no need for users to manually run login/logout commands. But maybe better to provide explicit methods for logging in and logging out, with unique session name that will be used for all commands to be executed (automatically discovering which commands need which type of sessions, ie FoD or SSC, and automatically adding the --*-session option with unique session name). Any created sessions should be logged out when rpc server terminates. I think FcliCommandExecutor can be configured to automatically add options if supported by a given command.

- Add fcli.ssc.login, fcli.ssc.logout for SSC sessions
- Add fcli.fod.login, fcli.fod.logout for FoD sessions
- Auto-logout all sessions when RPC server terminates
- Auto-add session options to commands based on product type
- Remove paging from fcli.execute (use async methods for large datasets)
- Improve argument quoting to prevent injection issues
- Fix logoutAll to avoid duplicate logout attempts
- Update documentation and tests

Co-authored-by: rsenden <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 26, 2025

@copilot Given 'fcli util rpc-server start' usage help, some potential improvements. The synchronous execute method shouldn't support paging; clients must use async execute method. Also, contrary to m...

Implemented in commit 94a2792:

  1. Removed paging from fcli.execute - now returns all results. Clients should use fcli.executeAsync + fcli.getPage for large datasets.

  2. Product-specific session methods (extensible architecture):

    • fcli.ssc.login - params: url, user+password / token / ci-token
    • fcli.ssc.logout
    • fcli.fod.login - params: url, client-id+client-secret / user+password+tenant
    • fcli.fod.logout
  3. Session management features:

    • Auto-logout all sessions when RPC server terminates
    • Auto-add --ssc-session / --fod-session options to commands
    • SC-SAST and SC-DAST share SSC session
  4. Extensible architecture: New products can be added by:

    • Adding a ProductType enum value
    • Creating login/logout handler classes
    • Registering handlers in RpcSessionManager.registerDefaultHandlers()

@rsenden
Copy link
Contributor

rsenden commented Nov 26, 2025

@copilot Session login/logout functions should also support insecure mode, and ssc login function needs to support optional client auth token (for sc-sast authentication) and drop support for ci-token (which is equivalent to 'token')

Per review feedback:
- Removed ci-token (equivalent to token)
- Added client-auth-token for SC-SAST authentication
- Added sc-sast-url for SC-SAST controller URL
- insecure mode already supported for both login methods

Co-authored-by: rsenden <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 26, 2025

@copilot Session login/logout functions should also support insecure mode, and ssc login function needs to support optional client auth token (for sc-sast authentication) and drop support for ci-token...

Implemented in commit 38cbca8:

  • SSC login now supports client-auth-token (for SC-SAST authentication) and sc-sast-url (optional SC-SAST controller URL)
  • Removed ci-token (since it's equivalent to token)
  • Both SSC and FoD login already support insecure mode (via -k flag)

Logout commands don't need the insecure flag as the session descriptors store the SSL settings from login.

@rsenden
Copy link
Contributor

rsenden commented Nov 26, 2025

@copilot Looks like you didn't update the usage help in UtilMessages.properties to reflect the updated and new RPC method signatures?

- Remove ci-token (now just use token for both UnifiedLoginToken and CIToken)
- Add client-auth-token and sc-sast-url for SC-SAST authentication
- Fix authentication note to reflect current implementation

Co-authored-by: rsenden <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 27, 2025

@copilot Looks like you didn't update the usage help in UtilMessages.properties to reflect the updated and new RPC method signatures?

Updated in commit 5638637:

  • Removed ci-token (now just use token for both UnifiedLoginToken and CIToken)
  • Added client-auth-token and sc-sast-url parameters for SC-SAST authentication
  • Fixed authentication note to reflect that only (user+password) or token are required

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