<commit_before>package wfe

import (
	"context"
	"crypto"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/mail"
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode"

	"gopkg.in/square/go-jose.v2"

	"github.com/jmhodges/clock"
	"github.com/letsencrypt/pebble/acme"
	"github.com/letsencrypt/pebble/ca"
	"github.com/letsencrypt/pebble/core"
	"github.com/letsencrypt/pebble/db"
	"github.com/letsencrypt/pebble/va"
)

const (
	// Note: We deliberately pick endpoint paths that differ from Boulder to
	// exercise clients processing of the /directory response
	// We export the DirectoryPath and RootCertPath so that the pebble binary can reference it
	DirectoryPath     = "/dir"
	RootCertPath      = "/root"
	noncePath         = "/nonce-plz"
	newAccountPath    = "/sign-me-up"
	acctPath          = "/my-account/"
	newOrderPath      = "/order-plz"
	orderPath         = "/my-order/"
	orderFinalizePath = "/finalize-order/"
	authzPath         = "/authZ/"
	challengePath     = "/chalZ/"
	certPath          = "/certZ/"
	revokeCertPath    = "/revoke-cert"
	keyRolloverPath   = "/rollover-account-key"

	// How long do pending authorizations last before expiring?
	pendingAuthzExpire = time.Hour

	// How many contacts is an account allowed to have?
	maxContactsPerAcct = 2

	// badNonceEnvVar defines the environment variable name used to provide
	// a percentage value for how often good nonces should be rejected as if they
	// were bad. This can be used to exercise client nonce handling/retries.
	// To have the WFE not reject any good nonces, run Pebble like:
	//   PEBBLE_WFE_NONCEREJECT=0 pebble
	// To have the WFE reject 15% of good nonces, run Pebble like:
	//   PEBBLE_WFE_NONCEREJECT=15 pebble
	badNonceEnvVar = "PEBBLE_WFE_NONCEREJECT"

	// By default when no PEBBLE_WFE_NONCEREJECT is set, what percentage of good
	// nonces are rejected?
	defaultNonceReject = 5

	// POST requests with a JWS body must have the following Content-Type header
	expectedJWSContentType = "application/jose+json"

	// RFC 1034 says DNS labels have a max of 63 octets, and names have a max of 255
	// octets: https://tools.ietf.org/html/rfc1035#page-10. Since two of those octets
	// are taken up by the leading length byte and the trailing root period the actual
	// max length becomes 253.
	maxDNSIdentifierLength = 253

	// Invalid revocation reason codes.
	// The full list of codes can be found in Section 8.5.3.1 of ITU-T X.509
	// http://www.itu.int/rec/T-REC-X.509-201210-I/en
	unusedRevocationReason       = 7
	aACompromiseRevocationReason = 10
)

type requestEvent struct {
	ClientAddr string `json:",omitempty"`
	Endpoint   string `json:",omitempty"`
	Method     string `json:",omitempty"`
	UserAgent  string `json:",omitempty"`
}

type wfeHandlerFunc func(context.Context, *requestEvent, http.ResponseWriter, *http.Request)

func (f wfeHandlerFunc) ServeHTTP(e *requestEvent, w http.ResponseWriter, r *http.Request) {
	ctx := context.TODO()
	f(ctx, e, w, r)
}

type wfeHandler interface {
	ServeHTTP(e *requestEvent, w http.ResponseWriter, r *http.Request)
}

type topHandler struct {
	wfe wfeHandler
}

func (th *topHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// TODO(@cpu): consider restoring X-Forwarded-For handling for ClientAddr
	rEvent := &requestEvent{
		ClientAddr: r.RemoteAddr,
		Method:     r.Method,
		UserAgent:  r.Header.Get("User-Agent"),
	}

	th.wfe.ServeHTTP(rEvent, w, r)
}

type WebFrontEndImpl struct {
	log             *log.Logger
	db              *db.MemoryStore
	nonce           *nonceMap
	nonceErrPercent int
	clk             clock.Clock
	va              *va.VAImpl
	ca              *ca.CAImpl
	strict          bool
}

const ToSURL = "data:text/plain,Do%20what%20thou%20wilt"

func New(
	log *log.Logger,
	clk clock.Clock,
	db *db.MemoryStore,
	va *va.VAImpl,
	ca *ca.CAImpl,
	strict bool) WebFrontEndImpl {

	// Read the % of good nonces that should be rejected as bad nonces from the
	// environment
	nonceErrPercentVal := os.Getenv(badNonceEnvVar)
	var nonceErrPercent int

	// Parse the env var value as a base 10 int - if there isn't an error, use it
	// as the wfe nonceErrPercent
	if val, err := strconv.ParseInt(nonceErrPercentVal, 10, 0); err == nil {
		nonceErrPercent = int(val)
	} else {
		// Otherwise just use the default
		nonceErrPercent = defaultNonceReject
	}

	// If the value is out of the range just clip it sensibly
	if nonceErrPercent < 0 {
		nonceErrPercent = 0
	} else if nonceErrPercent > 100 {
		nonceErrPercent = 99
	}
	log.Printf("Configured to reject %d%% of good nonces", nonceErrPercent)

	return WebFrontEndImpl{
		log:             log,
		db:              db,
		nonce:           newNonceMap(),
		nonceErrPercent: nonceErrPercent,
		clk:             clk,
		va:              va,
		ca:              ca,
		strict:          strict,
	}
}

func (wfe *WebFrontEndImpl) HandleFunc(
	mux *http.ServeMux,
	pattern string,
	handler wfeHandlerFunc,
	methods ...string) {

	methodsMap := make(map[string]bool)
	for _, m := range methods {
		methodsMap[m] = true
	}

	if methodsMap["GET"] && !methodsMap["HEAD"] {
		// Allow HEAD for any resource that allows GET
		methods = append(methods, "HEAD")
		methodsMap["HEAD"] = true
	}

	methodsStr := strings.Join(methods, ", ")
	defaultHandler := http.StripPrefix(pattern,
		&topHandler{
			wfe: wfeHandlerFunc(func(ctx context.Context, logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
				response.Header().Set("Replay-Nonce", wfe.nonce.createNonce())

				logEvent.Endpoint = pattern
				if request.URL != nil {
					logEvent.Endpoint = path.Join(logEvent.Endpoint, request.URL.Path)
				}

				addNoCacheHeader(response)

				if !methodsMap[request.Method] {
					response.Header().Set("Allow", methodsStr)
					wfe.sendError(acme.MethodNotAllowed(), response)
					return
				}

				wfe.log.Printf("%s %s -> calling handler()\n", request.Method, logEvent.Endpoint)

				// TODO(@cpu): Configurable request timeout
				timeout := 1 * time.Minute<commit_msg>WFE: Only send Replay-Nonce for POSTs/newNonce endpoint. (#184)<commit_after>package wfe

import (
	"context"
	"crypto"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/mail"
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode"

	"gopkg.in/square/go-jose.v2"

	"github.com/jmhodges/clock"
	"github.com/letsencrypt/pebble/acme"
	"github.com/letsencrypt/pebble/ca"
	"github.com/letsencrypt/pebble/core"
	"github.com/letsencrypt/pebble/db"
	"github.com/letsencrypt/pebble/va"
)

const (
	// Note: We deliberately pick endpoint paths that differ from Boulder to
	// exercise clients processing of the /directory response
	// We export the DirectoryPath and RootCertPath so that the pebble binary can reference it
	DirectoryPath     = "/dir"
	RootCertPath      = "/root"
	noncePath         = "/nonce-plz"
	newAccountPath    = "/sign-me-up"
	acctPath          = "/my-account/"
	newOrderPath      = "/order-plz"
	orderPath         = "/my-order/"
	orderFinalizePath = "/finalize-order/"
	authzPath         = "/authZ/"
	challengePath     = "/chalZ/"
	certPath          = "/certZ/"
	revokeCertPath    = "/revoke-cert"
	keyRolloverPath   = "/rollover-account-key"

	// How long do pending authorizations last before expiring?
	pendingAuthzExpire = time.Hour

	// How many contacts is an account allowed to have?
	maxContactsPerAcct = 2

	// badNonceEnvVar defines the environment variable name used to provide
	// a percentage value for how often good nonces should be rejected as if they
	// were bad. This can be used to exercise client nonce handling/retries.
	// To have the WFE not reject any good nonces, run Pebble like:
	//   PEBBLE_WFE_NONCEREJECT=0 pebble
	// To have the WFE reject 15% of good nonces, run Pebble like:
	//   PEBBLE_WFE_NONCEREJECT=15 pebble
	badNonceEnvVar = "PEBBLE_WFE_NONCEREJECT"

	// By default when no PEBBLE_WFE_NONCEREJECT is set, what percentage of good
	// nonces are rejected?
	defaultNonceReject = 5

	// POST requests with a JWS body must have the following Content-Type header
	expectedJWSContentType = "application/jose+json"

	// RFC 1034 says DNS labels have a max of 63 octets, and names have a max of 255
	// octets: https://tools.ietf.org/html/rfc1035#page-10. Since two of those octets
	// are taken up by the leading length byte and the trailing root period the actual
	// max length becomes 253.
	maxDNSIdentifierLength = 253

	// Invalid revocation reason codes.
	// The full list of codes can be found in Section 8.5.3.1 of ITU-T X.509
	// http://www.itu.int/rec/T-REC-X.509-201210-I/en
	unusedRevocationReason       = 7
	aACompromiseRevocationReason = 10
)

type requestEvent struct {
	ClientAddr string `json:",omitempty"`
	Endpoint   string `json:",omitempty"`
	Method     string `json:",omitempty"`
	UserAgent  string `json:",omitempty"`
}

type wfeHandlerFunc func(context.Context, *requestEvent, http.ResponseWriter, *http.Request)

func (f wfeHandlerFunc) ServeHTTP(e *requestEvent, w http.ResponseWriter, r *http.Request) {
	ctx := context.TODO()
	f(ctx, e, w, r)
}

type wfeHandler interface {
	ServeHTTP(e *requestEvent, w http.ResponseWriter, r *http.Request)
}

type topHandler struct {
	wfe wfeHandler
}

func (th *topHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// TODO(@cpu): consider restoring X-Forwarded-For handling for ClientAddr
	rEvent := &requestEvent{
		ClientAddr: r.RemoteAddr,
		Method:     r.Method,
		UserAgent:  r.Header.Get("User-Agent"),
	}

	th.wfe.ServeHTTP(rEvent, w, r)
}

type WebFrontEndImpl struct {
	log             *log.Logger
	db              *db.MemoryStore
	nonce           *nonceMap
	nonceErrPercent int
	clk             clock.Clock
	va              *va.VAImpl
	ca              *ca.CAImpl
	strict          bool
}

const ToSURL = "data:text/plain,Do%20what%20thou%20wilt"

func New(
	log *log.Logger,
	clk clock.Clock,
	db *db.MemoryStore,
	va *va.VAImpl,
	ca *ca.CAImpl,
	strict bool) WebFrontEndImpl {

	// Read the % of good nonces that should be rejected as bad nonces from the
	// environment
	nonceErrPercentVal := os.Getenv(badNonceEnvVar)
	var nonceErrPercent int

	// Parse the env var value as a base 10 int - if there isn't an error, use it
	// as the wfe nonceErrPercent
	if val, err := strconv.ParseInt(nonceErrPercentVal, 10, 0); err == nil {
		nonceErrPercent = int(val)
	} else {
		// Otherwise just use the default
		nonceErrPercent = defaultNonceReject
	}

	// If the value is out of the range just clip it sensibly
	if nonceErrPercent < 0 {
		nonceErrPercent = 0
	} else if nonceErrPercent > 100 {
		nonceErrPercent = 99
	}
	log.Printf("Configured to reject %d%% of good nonces", nonceErrPercent)

	return WebFrontEndImpl{
		log:             log,
		db:              db,
		nonce:           newNonceMap(),
		nonceErrPercent: nonceErrPercent,
		clk:             clk,
		va:              va,
		ca:              ca,
		strict:          strict,
	}
}

func (wfe *WebFrontEndImpl) HandleFunc(
	mux *http.ServeMux,
	pattern string,
	handler wfeHandlerFunc,
	methods ...string) {

	methodsMap := make(map[string]bool)
	for _, m := range methods {
		methodsMap[m] = true
	}

	if methodsMap["GET"] && !methodsMap["HEAD"] {
		// Allow HEAD for any resource that allows GET
		methods = append(methods, "HEAD")
		methodsMap["HEAD"] = true
	}

	methodsStr := strings.Join(methods, ", ")
	defaultHandler := http.StripPrefix(pattern,
		&topHandler{
			wfe: wfeHandlerFunc(func(ctx context.Context, logEvent *requestEvent, response http.ResponseWriter, request *http.Request) {
				// Modern ACME only sends a Replay-Nonce in responses to GET/HEAD
				// requests to the dedicated newNonce endpoint, or in replies to POST
				// requests that consumed a nonce.
				if request.Method == "POST" || pattern == noncePath {
					response.Header().Set("Replay-Nonce", wfe.nonce.createNonce())
				}

				logEvent.Endpoint = pattern
				if request.URL != nil {
					logEvent.Endpoint = path.Join(logEvent.Endpoint, request.URL.Path)
				}

				addNoCacheHeader(response)

				if !methodsMap[request.Method] {
					response.Header().Set("Allow", methodsStr)
					wfe.sendError(acme.MethodNotAllowed(), response)
					return
				}

				wfe.log.Printf("%s %s -> calling handler()\n", request.Method, logEvent.Endpoint)

				// TODO(@cpu): Configurable request timeout
				timeout := 1 * time.Minute
{'fraction_non_alphanumeric': 0.10965052901571017, 'fraction_numerical': 0.011782622635460083, 'mean_word_length': 3.2137791286727455, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3539893', 'n_tokens_mistral': 4551, 'n_tokens_neox': 4217, 'n_words': 1187}