Skip to content

Commit

Permalink
Merge pull request #131 from tomtom5152/cert-aia
Browse files Browse the repository at this point in the history
Add the ability for html test to accept servers using AIA.
  • Loading branch information
wjdp authored Dec 15, 2019
2 parents 1513352 + 7cb03ea commit 3a38fae
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 2 deletions.
14 changes: 14 additions & 0 deletions htmltest/check-link.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package htmltest

import (
"crypto/x509"
"fmt"
"github.com/wjdp/htmltest/htmldoc"
"github.com/wjdp/htmltest/issues"
"github.com/wjdp/htmltest/output"
"golang.org/x/net/html"
"net/http"
"net/url"
"os"
"path"
"strings"
Expand Down Expand Up @@ -197,6 +199,18 @@ func (hT *HTMLTest) checkExternal(ref *htmldoc.Reference) {
return
}

if certErr, ok := err.(*url.Error).Err.(x509.UnknownAuthorityError); ok {
err = validateCertChain(certErr.Cert)
if err == nil {
hT.issueStore.AddIssue(issues.Issue{
Level: issues.LevelWarning,
Reference: ref,
Message: "incomplete certificate chain",
})
return
}
}

// Unhandled client error, return generic error
hT.issueStore.AddIssue(issues.Issue{
Level: issues.LevelError,
Expand Down
10 changes: 9 additions & 1 deletion htmltest/check-link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func TestAnchorExternalInsecureOptionIgnored(t *testing.T) {
hT := tTestFileOpts("fixtures/links/issues/94.html",
map[string]interface{}{
"EnforceHTTPS": true,
"IgnoreURLs": []interface{}{"plantuml.com", "plantuml.net", "forum.plantuml.net"},
"IgnoreURLs": []interface{}{"plantuml.com", "plantuml.net", "forum.plantuml.net"},
})
tExpectIssueCount(t, hT, 0)
}
Expand Down Expand Up @@ -158,6 +158,14 @@ func TestAnchorExternalHTTPSInvalid(t *testing.T) {
tExpectIssueCount(t, hT, 6)
}

func TestAnchorExternalHTTPSMissingChain(t *testing.T) {
// should support https aia
// see issue #130
hT := tTestFileOpts("fixtures/links/https-incomplete-chain.html",
map[string]interface{}{"VCREnable": true})
tExpectIssue(t, hT, "incomplete certificate chain", 1)
}

func TestAnchorExternalHTTPSBadH2(t *testing.T) {
// should connect to servers with bad http/2 support
// See issue #49
Expand Down
12 changes: 12 additions & 0 deletions htmltest/fixtures/links/https-incomplete-chain.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>

<body>

<p>Blah blah blah. <a href="https://github.com/octocat/Spoon-Knife/issues">An HTTPS link!</a></p>

<p>
<a href="https://incomplete-chain.badssl.com/">incomplete-chain</a>
</p>
</body>

</html>
73 changes: 72 additions & 1 deletion htmltest/util.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,78 @@
package htmltest

import "net/http"
import (
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
)

type CertChainErr struct {
cert *x509.Certificate
chain *x509.CertPool
hintErr error
}

func (e CertChainErr) Error() string {
s := "x509: could not validate certificate chain"
if e.hintErr != nil {
s += fmt.Sprintf(" (possibly because of %q)", e.hintErr)
}
return s
}

func statusCodeValid(code int) bool {
return code == http.StatusPartialContent || code == http.StatusOK
}

func validateCertChain(cert *x509.Certificate) (err error) {
if cert.IssuingCertificateURL == nil {
return CertChainErr{cert: cert}
}

intermediates := x509.NewCertPool()
//roots, err := x509.SystemCertPool()
if err != nil {
return CertChainErr{cert: cert}
}
var certsToFetch []string = cert.IssuingCertificateURL

for i := 0; i < len(certsToFetch); i++ {
url := certsToFetch[i]

resp, err := http.Get(url)
if err != nil {
return CertChainErr{cert: cert, chain: intermediates, hintErr: err}
}

if resp.StatusCode != 200 {
return CertChainErr{cert: cert, chain: intermediates, hintErr: fmt.Errorf("could not fetch certificate at %s (status %d)", url, resp.StatusCode)}
}

certBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return CertChainErr{cert: cert, chain: intermediates, hintErr: err}
}

newCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return CertChainErr{cert: cert, chain: intermediates, hintErr: err}
}

if newCert.CheckSignatureFrom(cert) == nil {
// we have out root
break
}

intermediates.AddCert(newCert)

if newCert.IssuingCertificateURL != nil {
certsToFetch = append(certsToFetch, newCert.IssuingCertificateURL...)
}
}

_, err = cert.Verify(x509.VerifyOptions{
Intermediates: intermediates,
})
return
}

0 comments on commit 3a38fae

Please sign in to comment.