Repository navigation
Fix SVG trophy generation in the GitHub Action: empty GraphQL body, null fields, token leak in logs, output path crash; add layout inputs - #458
Open
satishjhanwer wants to merge 5 commits into
Conversation
soxa.post(url, data, config) uses the explicit data argument over
config.data when merging, so passing {} as data and {query, variables}
inside config.data silently sent an empty body to the GitHub GraphQL
API on every request.
GitHub's GraphQL API can return null for fields like organizations when the token lacks the relevant scope, and null entries inside repositories.nodes for repos it can't fully resolve for the given token. UserInfo's constructor did unguarded property access on these, throwing TypeErrors that were silently swallowed by an empty catch block elsewhere. Default missing sub-fields to 0/[] and filter null nodes out of repositories.nodes before use.
requestUserInfo had a bare catch {} that discarded the actual thrown
error, making failures impossible to diagnose from CI logs. Fixing
that surfaced a second issue: soxa attaches the full request config
(including the Authorization bearer token header) onto thrown errors
and defines toJSON() to re-serialize it, and executeQuery/Retry logged
raw error objects or JSON.stringify(error.cause), which would print
that header into the Actions log. Added safeErrorMessage(), which only
ever extracts .message from a thrown value, and routed all error
logging in these files through it.
- request.ts: assert the POST body actually sent to soxa.post is
{query, variables}, not {} - fails on the pre-fix code with the
exact empty-body bug.
- user_info.ts: assert UserInfo defaults null/missing GraphQL
sub-fields instead of throwing - fails on the pre-fix code with the
exact 'Cannot read properties of null' crash seen in production.
- safeErrorMessage.ts: unit tests for the new helper, including
asserting it never surfaces extra properties (e.g. a token in
error.config) attached to an Error.
Verified both regression tests fail against the original buggy
src/Services/request.ts and src/user_info.ts before being fixed, and
that the full suite (15 tests) passes with the fixes applied.
|
awesome hopefully this is picked up soon |
Author
|
Please do let me know, if anything else is required from my side. |
Author
|
@ryo-ma Hey if you get a chance, could you please take a look at this PR and merge it if its all good. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Running the
render_svg.ts/ composite action onmastercurrently fails for every user with a generic, unhelpful error:The real errors were being swallowed by an empty
catch {}block. After fixing the logging, two separate root causes surfaced, plus a token-leak risk uncovered while fixing the logging. Getting the action to run end to end then exposed an output-path crash, and the layout options couldn't be set from the action at all. This PR fixes all of it.1. GraphQL request body was always empty
src/Services/request.tscalls:soxamirrors axios'spost(url, data, config)signature, where the explicitdataargument (2nd param) always wins overconfig.dataduring merge. So the actual POST body sent toapi.github.com/graphqlwas{}- thequery/variablesinconfig.datawere silently discarded. GitHub's API rejects that, causing every request to fail regardless of token or username.Fix: pass
{ query, variables }as the actualdataargument.2. Unguarded property access on optional/missing GraphQL fields
Once (1) was fixed,
UserInfo.fromCombined()insrc/user_info.tscrashed withTypeError: Cannot read properties of null. GitHub's GraphQL API can returnnullfor fields likeorganizationswhen the token lacks the relevant scope (a known API behavior), andnullentries insiderepositories.nodesfor repos it can't fully resolve for the given token. The constructor did unguarded access likeuserActivity.organizations.totalCountandnode.stargazerCountwith no null checks anywhere.Fix: default missing sub-fields to
0/[], and filternullentries out ofrepositories.nodesbefore use.3. Errors were logged with no detail, which hid both of the above
GithubApiService.requestUserInfohad a barecatch { Logger.error("generic message") }that discarded the actual thrown error, making this impossible to diagnose from CI logs alone.Fix: log the actual error too.
4. Fixing (3) risked leaking the auth token into logs
While fixing (3), found that
soxaattaches the full requestconfig- including theAuthorization: bearer <token>header - onto every thrown error, and even definestoJSON()to re-serialize it. Several catch blocks (GithubApiService.ts,Retry.ts) logged the raw error object orJSON.stringify(error.cause), which would print that header straight into the Actions log.Fix: added
src/Helpers/safeErrorMessage.ts, which extracts only.messagefrom any thrown value. Routed all error logging through it instead of logging raw error/cause objects.5. Action crashes when
output_pathis a bare filenameWith the fixes above, the action fetched data and rendered the SVG, but then failed when writing it out with the default
output_path: trophy.svg:render_svg.tsfound the parent directory withoutputPath.replace(/\/[^/]+$/, ""). When the path has no/, that returns the whole filename, so the script ranDeno.mkdir("trophy.svg")and then tried to write a file onto that directory.Fix: use
posix.dirname()from std, and skipmkdirwhen the result is.(e.g.trophy.svg,./trophy.svg).6. Layout options were hard-coded and couldn't be set from the action
action.ymlonly exposedusername,output_path,tokenandtheme. Columns, rows, margins and the other options were constants inrender_svg.ts, so a workflow passingmax-colsormargin-widthonly got an "Unexpected input(s)" warning and the setting was ignored.Fix: added the inputs
max-cols,max-rows,margin-width,margin-height,panel-size,no-bg,no-frame,titleandrank.TROPHY_*environment variables, so the positional CLI arguments don't change and no input is put into the shell command.titleandranktake comma-separated values and support the-exclusion prefix, the same as the web API.src/Helpers/renderOptions.tsso they can be unit-tested. The README action example lists the new inputs.Testing
src/Helpers/__tests__/renderOptions.test.tsadds 6 tests: a regression test for the bare-filename crash, nested output paths, defaults, empty action inputs, value parsing (numbers, booleans, comma-separated lists) and fallback for invalid values.deno test: 21 passed, 0 failed.deno checkanddeno fmt --checkare clean.trophy.svgwas generated after each fix, and that the sanitized logging still shows useful error messages without exposing the token.