{"record":{"id":"4ac4ecf85a404ad7","repo":"iflytek/astron-agent","slug":"unsupported-workflow-gateway-request","errorCode":null,"errorMessage":"unsupported workflow gateway request","messagePattern":"unsupported workflow gateway request","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"console/backend/commons/src/main/java/com/iflytek/astron/console/commons/security/WorkflowGatewayIdentity.java","lineNumber":31,"sourceCode":"\n    public static final String TIMESTAMP_HEADER = \"X-Workflow-Gateway-Timestamp\";\n    public static final String SIGNATURE_HEADER = \"X-Workflow-Gateway-Signature\";\n\n    private static final String POST = \"POST\";\n    private static final String HMAC_SHA_256 = \"HmacSHA256\";\n    private static final Set<String> PUBLIC_WORKFLOW_PATHS = Set.of(\n            \"/workflow/v1/chat/completions\", \"/workflow/v1/resume\");\n\n    private WorkflowGatewayIdentity() {}\n\n    /**\n     * Validate the original public request metadata and return the exact path bound into the signature.\n     * Query parameters are deliberately excluded; no decoding or path normalization is performed, so\n     * encoded or alternate paths fail closed.\n     */\n    public static String requireAuthorizedPath(String originalMethod, String originalUri) {\n        if (!POST.equals(originalMethod) || StringUtils.isEmpty(originalUri)) {\n            throw new IllegalArgumentException(\"unsupported workflow gateway request\");\n        }\n        int queryStart = originalUri.indexOf('?');\n        String path = queryStart < 0 ? originalUri : originalUri.substring(0, queryStart);\n        if (!PUBLIC_WORKFLOW_PATHS.contains(path) || originalUri.indexOf('#') >= 0) {\n            throw new IllegalArgumentException(\"unsupported workflow gateway request\");\n        }\n        return path;\n    }\n\n    /** Sign {@code method + newline + path + newline + appId + newline + epochSeconds}. */\n    public static String sign(\n            String configuredKey,\n            String method,\n            String path,\n            String appId,\n            long epochSeconds) {\n        String internalKey = WorkflowInternalApiKey.requireConfigured(configuredKey);\n        if (!POST.equals(method)","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/security/WorkflowGatewayIdentity.java#L13-L49","documentation":"WorkflowGatewayIdentity.requireAuthorizedPath validates the original public request metadata before signature binding: the request must be a POST to one of the whitelisted PUBLIC_WORKFLOW_PATHS, with no fragment. Anything else fails closed with IllegalArgumentException. Query strings are stripped, never decoded or normalized, so encoded or alternate paths are rejected.","triggerScenarios":"Forwarding a GET/PUT/DELETE request through the workflow gateway; passing an empty or null originalUri; a URI whose path is not in PUBLIC_WORKFLOW_PATHS; a URI containing a '#' fragment; percent-encoded path variants that don't byte-match the whitelist entries.","commonSituations":"Clients hitting non-whitelisted workflow endpoints through the public gateway; proxies rewriting or encoding the URI; misconfigured gateways forwarding fragments; version changes that moved a path off the whitelist.","solutions":["Use POST against an exact whitelisted PUBLIC_WORKFLOW_PATHS entry, with no fragment in the URI","Check the request URI for encoding/rewriting by proxies; send the raw original URI","If the endpoint should be public, add its exact path to PUBLIC_WORKFLOW_PATHS (security-reviewed change)","Catch IllegalArgumentException in the gateway filter and return 400/403"],"exampleFix":"// before\nString path = WorkflowGatewayIdentity.requireAuthorizedPath(request.getMethod(), request.getRequestURI()); // GET /api/workflow/run\n// after\n// Only POST to an exact public path:\nif (\"POST\".equals(request.getMethod()) && request.getRequestURI().startsWith(\"/api/workflow/public\")) {\n    String path = WorkflowGatewayIdentity.requireAuthorizedPath(request.getMethod(), request.getRequestURI());\n}","handlingStrategy":"validation","validationCode":"boolean ok = \"POST\".equals(method) && uri != null && !uri.isEmpty() && !uri.contains(\"#\") && PUBLIC_WORKFLOW_PATHS.contains(uri.split(\"\\\\?\")[0]);","typeGuard":null,"tryCatchPattern":"try { path = WorkflowGatewayIdentity.requireAuthorizedPath(method, uri); } catch (IllegalArgumentException e) { response.sendError(400); }","preventionTips":["Only route whitelisted POST endpoints through the workflow gateway","Pass the original raw URI; never normalize or decode it first","Keep the client's path list synchronized with PUBLIC_WORKFLOW_PATHS"],"tags":["validation","gateway","whitelist"],"backgroundTag":"invalid-argument-value","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}