Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove logging of any SAS tokens in Actions/Cache and Actions/Artifact #1982

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

salmanmkc
Copy link

@salmanmkc salmanmkc commented Mar 10, 2025

Issue

Currently you are able to see the SAS token for downloading and uploading files when debugging.

This is not secure as technically anyone with access to the logs can use the SAS token to download or upload files.

Fix

Fix: masking the SAS token, so that you are unable to see it anymore.

Examples:

Cache

Cache uploading
image

Cache downloading
image

Artifact

Uploading to artifact
image

Downloading from artifact
image

Questions

Discussion outcome was to have duplicate code rather than using a shared utility function in Core, which was the previous approach as indicated via previous commits.

This PR will need a release of:

@salmanmkc salmanmkc requested review from a team as code owners March 10, 2025 14:10
* @returns A masked URL where the sig parameter value is replaced with '***' if found,
* or the original URL if no sig parameter is present.
*/
maskSigUrl(url: string): string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of having this as a method on clients (therefore needing to export/test the entire client) how about making a singular maskSignedURLSignature utility function?

Copy link
Author

@salmanmkc salmanmkc Mar 10, 2025

Choose a reason for hiding this comment

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

Yeah, tried to do that here, discussed with @Link- and was told it's ok to have duplicated code, so reverted the approach with the utility function and put it into each client. Ran into some issue with testing it locally using npm link for some reason. Logically I think it should work with the utility function in core, so open to trying that out, but hit that issue with the test saying the utility function doesn't exist, despite the compiled code showing it and the typescript compiler not complaining.

Copy link
Member

Choose a reason for hiding this comment

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

This is the same conversation we had for cache / artifacts regarding the clients and shared components. It might not be worth it to block this small fix by a larger effort to address the use of a shared library

Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't necessarily mean making it shared across packages, more of a utility function in each. Since it's on the client, we're exporting it now (for tests?). It doesn't need to be on the client class since it it doesn't use any class attributes and could be easier to test just that part.

But this is all nonblocking for the change

Copy link
Author

Choose a reason for hiding this comment

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

Ah okay, ya that sounds like a good idea, I can put it in util.ts

Comment on lines +158 to +166
maskSigUrl(url: string): string {
const sigIndex = url.indexOf('sig=')
if (sigIndex !== -1) {
const sigValue = url.substring(sigIndex + 4)
setSecret(sigValue)
return `${url.substring(0, sigIndex + 4)}***`
}
return url
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Unless the signature is guaranteed to be the last query parameter, this will mask more than the signature right?

How about something like:

const params = new URL(url).searchParams
const signature = params.get("sig")
setSecret(signature)
setSecret(encodeURIComponent(signature))
// ...

But this way we mask just the signature (and it's encoded URI version) so if the runner ends up printing just the sig param or somehow out of order parameters the mask will still work.

(Also you'll need to catch the new URL exception if we don't pass a valid url string)

Copy link
Author

Choose a reason for hiding this comment

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

Yeah previously I had that, this was a previous workflow run:
image
Code: 47c4fa8#diff-ad88f3760254954a760927536fc9ca883893c68d7662005a09bf32abd9d500cc

@Link- thoughts? I know you said you preferred masking in the current approach vs just the sig param

Copy link
Member

@Link- Link- Mar 11, 2025

Choose a reason for hiding this comment

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

I know you said you preferred masking in the current approach vs just the sig param

When we discussed this, we spoke about the potential ways of abuse & what makes sense to mask vs what makes sense to keep for troubleshooting purposes. I don't believe we discussed specific instructions on how to write the code for that :)

@robherley's suggestion solves for the edge cases of masking the sig field only. The implementation that he commented on needs to be revisited irrespective of whether we mask the signature only or other parts of the URL, because you have no guarantees where that parameter will be.

The question remains, is masking the signature alone sufficient or do we need to mask further fields? What is the appropriate tradeoff here?

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