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

Added SignTool for v5 #2444

Merged
merged 16 commits into from
Mar 5, 2020
Merged

Added SignTool for v5 #2444

merged 16 commits into from
Mar 5, 2020

Conversation

jhromadik
Copy link
Contributor

Added SignTool for FAKE v5, some documentation, unit tests and updated obsolete information for the old SignToolHelper.

If someone could please have a look to make sure everything is OK ... Other than that, I think it's ready.

@matthid matthid changed the base branch from master to release/next December 18, 2019 18:31
@matthid
Copy link
Member

matthid commented Dec 18, 2019

Thanks @jhromadik!

Can you please resolve the conflicts?

One thing I already noticed: Usually API design is to let users input a function and change arguments. You let users call Options.Create and then modify the record. Any particular reason to not follow what other modules do?

…tion parameter (default) and the other taking an options record parameter (o suffix)
@jhromadik
Copy link
Contributor Author

Conflicts resolved.

As to my API design choice ... I'm not sure. I thought that I had looked at other projects to see what they were doing, concluded that it was about 50/50, and decided to go with the record-style. Looking through the projects now, I see that everyone is doing the function-style. So now I don't know what I was thinking.

Anyway, I reworked all functions to the function-style by default. I also kept the record-style functions as alternatives, same name only with an 'o' suffix, should anyone prefer to call them that way.

@matthid
Copy link
Member

matthid commented Dec 20, 2019

One problem with the regular record is when we add fields which we don't consider a breaking change. If people construct the record by hand (by specifying all fields and not using the create function) the code will no longer compile. So usung the functions kind of forces people to use the correct syntax without them having to know about the create function.
I'm not saying that we cannot provide those overloads I'm probably just not yet fully convinced :)

@jhromadik
Copy link
Contributor Author

Only function-style functions remain. They have better usability, and if you have a record you want to use, you can still do that - took me a while to realize this :). Added random string generation to tests, since FsCheck doesn't do that by default.

Copy link
Member

@matthid matthid left a comment

Choose a reason for hiding this comment

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

Ok thanks again! I did a full review now and I think some things are still old because of the port from the legacy SignToolHelper API?
We should consider fixing them as well, otherwise it looks reasonable. Especially thanks for the docs. That made reviewing easier :)

Let me know what you think.

// val sign : certificate:SignTool.SignCertificate -> setOptions:(SignTool.SignOptions -> SignTool.SignOptions) -> files:seq<string> -> unit
SignTool.sign
(SignTool.SignCertificate.Store {
SignTool.CertificateFromStore.Create() with
Copy link
Member

Choose a reason for hiding this comment

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

(minor) This looks a bit strange, on the other hand I thought about it and couldn't come up with something I really liked.

The "best" might be a CreateStore helper function which takes a creation function:

SignTool.SignCertificate.FromStore ("path/to/certificate-file.pfx", fun opts ->
   { opts with
            Password = Some "pw" })

or

SignTool.SignCertificate.FromFile (fun opts ->
   { opts with
            AutomaticallySelectCertificate = Some true
            SubjectName = Some "subject"
            StoreName = Some "My" })

Which might be a bit more familiar and you don't need to call Create by hand.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was trying to find a way to make this "prettier" but came up empty. I like your suggestion and will implement it, thanks.

SignTool.timeStamp
(fun o ->
{ o with
TimeStamp = Some { SignTool.TimeStampOption.Create() with
Copy link
Member

Choose a reason for hiding this comment

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

Again: Quite Unusual
Maybe add overloads of the Create function? (One with only the server and one with server & algorithm).
Not sure what the most likely scenario is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I'll just pull ServerUrl and Algorithm up to the options level.

Default parameters:

SignTool.timeStamp (fun o -> o)

With additional parameters:

SignTool.timeStamp
    (fun o -> { o with
                    ServerUrl = ...
                    Algorithm = ... } )

ToolPath: string option
/// Timeout.
/// If not provided, default value is 10 seconds per file.
Timeout: TimeSpan option
Copy link
Member

Choose a reason for hiding this comment

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

I would expect Timeout = None to make the timeout disabled. Or is there a default by the tool itself (when no parameter is given)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was just trying to set a reasonable default value. In retrospect, it is unreasonable and more confusing than useful. I'll remove the default value and there will be no timeout when set to None.

// set path to signtool.exe - if you want to use a specific version or you don't have Windows SDKs installed
// by default, an attempt will be made to locate it automatically in 'Program Files (x86)\Windows Kits'
{ o with
ToolOptions = Some { SignTool.ToolOptions.Create() with
Copy link
Member

Choose a reason for hiding this comment

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

Same as above in addition to the suggestions above another way would be to "inline" all the record fields. With the downside of duplicated code on the implementation (you could use SRTP - statically resolved type parameter to work around this)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll "inline" these. It will be much prettier on the surface and should only be a minor implementation inconvenience.

let serverUrl = t.ServerUrl |> Option.defaultValue "http://timestamp.digicert.com"
yield match t.Algorithm with
| Some SHA1 -> sprintf "/t \"%s\"" serverUrl
| Some SHA256 | None -> sprintf "/tr \"%s\" /td \"sha256\"" serverUrl
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer to use the Arguments API as (briefly) documented in https://fake.build/core-process.html and https://fake.build/apidocs/v5/fake-core-argumentsmodule.html
It handles proper escaping and edge cases accordingly

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll look into it.

| true, File f ->
if f.Password.IsSome then
// surround in quotes to lower chances of replacing non-password occurences of password-string
TraceSecrets.register "\"<PASSWORD>\"" (sprintf "\"%s\"" f.Password.Value)
Copy link
Member

Choose a reason for hiding this comment

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

I guess you mean if a very simple password is used and replaced elsewhere?
This is a known problem, even on build servers which support secret variables (like azure devops)
I'm not sure we need special casing here, this might lead to the password being printed when it is not quoted?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I ran into this when I was using a test certificate where the password was also in the file name (to make sure I didn't forget it) - part of the file name was replaced with the registered replacement which looked pretty weird.
The implementation makes sure it is always quoted and there is a test for it too, so erroneously displaying the password should not be a problem.

@Robin1985
Copy link
Contributor

Hi! Thanks for the contribution, is there anything I can do to help get this merged? I was just about to port this but then saw you already had a pull request for it.

@matthid
Copy link
Member

matthid commented Mar 3, 2020

I hope to find the time to prepare a new release within a couple of days. Either all points are addressed (or only small things which I can do myself are open) and it will be part of the release or new comments will follow :)

- using the newest Windows SDK available
- working around ProcessUtils.tryFindFile's lack of globbing support
@jhromadik
Copy link
Contributor Author

I think I've addressed all comments:

  • added CreateFromFile and CreateFromStore static helpers
  • made signing with timestamp a separate function singWithTimeStamp with additional parameters
  • no Timeout means no timeout
  • "inlined" ToolOptions
  • revorked implementation to use Arguments API

Plus some additional work:

  • removed all defaults that were different from signtool.exe's defaults
  • improved test to make sure final arguments only contain exactly what they are supposed to contain

@matthid, @Robin1985 please have a look at it and let me know what you think.

@matthid matthid merged commit 10cd9bd into fsprojects:release/next Mar 5, 2020
@matthid matthid mentioned this pull request Mar 5, 2020
3 tasks
@matthid
Copy link
Member

matthid commented Mar 5, 2020

Looks good to me, thanks a lot!

@jhromadik jhromadik deleted the signtool-for-v5 branch March 9, 2020 07:50
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