 131868bdca
			
		
	
	131868bdca
	
	
	
		
			
			Major security, observability, and configuration improvements:
## Security Hardening
- Implemented configurable CORS (no more wildcards)
- Added comprehensive auth middleware for admin endpoints
- Enhanced webhook HMAC validation
- Added input validation and rate limiting
- Security headers and CSP policies
## Configuration Management
- Made N8N webhook URL configurable (WHOOSH_N8N_BASE_URL)
- Replaced all hardcoded endpoints with environment variables
- Added feature flags for LLM vs heuristic composition
- Gitea fetch hardening with EAGER_FILTER and FULL_RESCAN options
## API Completeness
- Implemented GetCouncilComposition function
- Added GET /api/v1/councils/{id} endpoint
- Council artifacts API (POST/GET /api/v1/councils/{id}/artifacts)
- /admin/health/details endpoint with component status
- Database lookup for repository URLs (no hardcoded fallbacks)
## Observability & Performance
- Added OpenTelemetry distributed tracing with goal/pulse correlation
- Performance optimization database indexes
- Comprehensive health monitoring
- Enhanced logging and error handling
## Infrastructure
- Production-ready P2P discovery (replaces mock implementation)
- Removed unused Redis configuration
- Enhanced Docker Swarm integration
- Added migration files for performance indexes
## Code Quality
- Comprehensive input validation
- Graceful error handling and failsafe fallbacks
- Backwards compatibility maintained
- Following security best practices
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
		
	
		
			
				
	
	
		
			194 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2019, 2020 OCI Contributors
 | |
| // Copyright 2017 Docker, Inc.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     https://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package digest
 | |
| 
 | |
| import (
 | |
| 	"crypto"
 | |
| 	"fmt"
 | |
| 	"hash"
 | |
| 	"io"
 | |
| 	"regexp"
 | |
| )
 | |
| 
 | |
| // Algorithm identifies and implementation of a digester by an identifier.
 | |
| // Note the that this defines both the hash algorithm used and the string
 | |
| // encoding.
 | |
| type Algorithm string
 | |
| 
 | |
| // supported digest types
 | |
| const (
 | |
| 	SHA256 Algorithm = "sha256" // sha256 with hex encoding (lower case only)
 | |
| 	SHA384 Algorithm = "sha384" // sha384 with hex encoding (lower case only)
 | |
| 	SHA512 Algorithm = "sha512" // sha512 with hex encoding (lower case only)
 | |
| 
 | |
| 	// Canonical is the primary digest algorithm used with the distribution
 | |
| 	// project. Other digests may be used but this one is the primary storage
 | |
| 	// digest.
 | |
| 	Canonical = SHA256
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	// TODO(stevvooe): Follow the pattern of the standard crypto package for
 | |
| 	// registration of digests. Effectively, we are a registerable set and
 | |
| 	// common symbol access.
 | |
| 
 | |
| 	// algorithms maps values to hash.Hash implementations. Other algorithms
 | |
| 	// may be available but they cannot be calculated by the digest package.
 | |
| 	algorithms = map[Algorithm]crypto.Hash{
 | |
| 		SHA256: crypto.SHA256,
 | |
| 		SHA384: crypto.SHA384,
 | |
| 		SHA512: crypto.SHA512,
 | |
| 	}
 | |
| 
 | |
| 	// anchoredEncodedRegexps contains anchored regular expressions for hex-encoded digests.
 | |
| 	// Note that /A-F/ disallowed.
 | |
| 	anchoredEncodedRegexps = map[Algorithm]*regexp.Regexp{
 | |
| 		SHA256: regexp.MustCompile(`^[a-f0-9]{64}$`),
 | |
| 		SHA384: regexp.MustCompile(`^[a-f0-9]{96}$`),
 | |
| 		SHA512: regexp.MustCompile(`^[a-f0-9]{128}$`),
 | |
| 	}
 | |
| )
 | |
| 
 | |
| // Available returns true if the digest type is available for use. If this
 | |
| // returns false, Digester and Hash will return nil.
 | |
| func (a Algorithm) Available() bool {
 | |
| 	h, ok := algorithms[a]
 | |
| 	if !ok {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	// check availability of the hash, as well
 | |
| 	return h.Available()
 | |
| }
 | |
| 
 | |
| func (a Algorithm) String() string {
 | |
| 	return string(a)
 | |
| }
 | |
| 
 | |
| // Size returns number of bytes returned by the hash.
 | |
| func (a Algorithm) Size() int {
 | |
| 	h, ok := algorithms[a]
 | |
| 	if !ok {
 | |
| 		return 0
 | |
| 	}
 | |
| 	return h.Size()
 | |
| }
 | |
| 
 | |
| // Set implemented to allow use of Algorithm as a command line flag.
 | |
| func (a *Algorithm) Set(value string) error {
 | |
| 	if value == "" {
 | |
| 		*a = Canonical
 | |
| 	} else {
 | |
| 		// just do a type conversion, support is queried with Available.
 | |
| 		*a = Algorithm(value)
 | |
| 	}
 | |
| 
 | |
| 	if !a.Available() {
 | |
| 		return ErrDigestUnsupported
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Digester returns a new digester for the specified algorithm. If the algorithm
 | |
| // does not have a digester implementation, nil will be returned. This can be
 | |
| // checked by calling Available before calling Digester.
 | |
| func (a Algorithm) Digester() Digester {
 | |
| 	return &digester{
 | |
| 		alg:  a,
 | |
| 		hash: a.Hash(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Hash returns a new hash as used by the algorithm. If not available, the
 | |
| // method will panic. Check Algorithm.Available() before calling.
 | |
| func (a Algorithm) Hash() hash.Hash {
 | |
| 	if !a.Available() {
 | |
| 		// Empty algorithm string is invalid
 | |
| 		if a == "" {
 | |
| 			panic(fmt.Sprintf("empty digest algorithm, validate before calling Algorithm.Hash()"))
 | |
| 		}
 | |
| 
 | |
| 		// NOTE(stevvooe): A missing hash is usually a programming error that
 | |
| 		// must be resolved at compile time. We don't import in the digest
 | |
| 		// package to allow users to choose their hash implementation (such as
 | |
| 		// when using stevvooe/resumable or a hardware accelerated package).
 | |
| 		//
 | |
| 		// Applications that may want to resolve the hash at runtime should
 | |
| 		// call Algorithm.Available before call Algorithm.Hash().
 | |
| 		panic(fmt.Sprintf("%v not available (make sure it is imported)", a))
 | |
| 	}
 | |
| 
 | |
| 	return algorithms[a].New()
 | |
| }
 | |
| 
 | |
| // Encode encodes the raw bytes of a digest, typically from a hash.Hash, into
 | |
| // the encoded portion of the digest.
 | |
| func (a Algorithm) Encode(d []byte) string {
 | |
| 	// TODO(stevvooe): Currently, all algorithms use a hex encoding. When we
 | |
| 	// add support for back registration, we can modify this accordingly.
 | |
| 	return fmt.Sprintf("%x", d)
 | |
| }
 | |
| 
 | |
| // FromReader returns the digest of the reader using the algorithm.
 | |
| func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
 | |
| 	digester := a.Digester()
 | |
| 
 | |
| 	if _, err := io.Copy(digester.Hash(), rd); err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	return digester.Digest(), nil
 | |
| }
 | |
| 
 | |
| // FromBytes digests the input and returns a Digest.
 | |
| func (a Algorithm) FromBytes(p []byte) Digest {
 | |
| 	digester := a.Digester()
 | |
| 
 | |
| 	if _, err := digester.Hash().Write(p); err != nil {
 | |
| 		// Writes to a Hash should never fail. None of the existing
 | |
| 		// hash implementations in the stdlib or hashes vendored
 | |
| 		// here can return errors from Write. Having a panic in this
 | |
| 		// condition instead of having FromBytes return an error value
 | |
| 		// avoids unnecessary error handling paths in all callers.
 | |
| 		panic("write to hash function returned error: " + err.Error())
 | |
| 	}
 | |
| 
 | |
| 	return digester.Digest()
 | |
| }
 | |
| 
 | |
| // FromString digests the string input and returns a Digest.
 | |
| func (a Algorithm) FromString(s string) Digest {
 | |
| 	return a.FromBytes([]byte(s))
 | |
| }
 | |
| 
 | |
| // Validate validates the encoded portion string
 | |
| func (a Algorithm) Validate(encoded string) error {
 | |
| 	r, ok := anchoredEncodedRegexps[a]
 | |
| 	if !ok {
 | |
| 		return ErrDigestUnsupported
 | |
| 	}
 | |
| 	// Digests much always be hex-encoded, ensuring that their hex portion will
 | |
| 	// always be size*2
 | |
| 	if a.Size()*2 != len(encoded) {
 | |
| 		return ErrDigestInvalidLength
 | |
| 	}
 | |
| 	if r.MatchString(encoded) {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return ErrDigestInvalidFormat
 | |
| }
 |