diff --git a/go.mod b/go.mod index caebd033a..3f22243b8 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/ggwhite/go-masker v1.1.0 github.com/go-chi/chi/v5 v5.2.1 github.com/go-chi/render v1.0.3 - github.com/go-ldap/ldap/v3 v3.4.10 + github.com/go-ldap/ldap/v3 v3.4.11 github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 github.com/go-micro/plugins/v4/client/grpc v1.2.1 github.com/go-micro/plugins/v4/logger/zerolog v1.2.0 @@ -51,7 +51,7 @@ require ( github.com/kovidgoyal/imaging v1.6.4 github.com/leonelquinteros/gotext v1.7.1 github.com/libregraph/idm v0.5.0 - github.com/libregraph/lico v0.65.1 + github.com/libregraph/lico v0.65.2-0.20250428103211-356e98f98457 github.com/mitchellh/mapstructure v1.5.0 github.com/mna/pigeon v1.3.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 @@ -179,7 +179,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/gdexlab/go-render v1.0.1 // indirect github.com/go-acme/lego/v4 v4.4.0 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/go-git/go-git/v5 v5.13.2 // indirect @@ -232,7 +232,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/juliangruber/go-intersect v1.1.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -282,7 +282,7 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rs/xid v1.6.0 // indirect - github.com/russellhaering/goxmldsig v1.4.0 // indirect + github.com/russellhaering/goxmldsig v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/kafka-go v0.4.47 // indirect github.com/segmentio/ksuid v1.0.4 // indirect @@ -345,8 +345,6 @@ replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/pl replace go-micro.dev/v4 => github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3 -replace github.com/libregraph/lico => github.com/dragonchaser/lico v0.0.0-20250416141658-4d60b0ff2e7d - // exclude the v2 line of go-sqlite3 which was released accidentally and prevents pulling in newer versions of go-sqlite3 // see https://github.com/mattn/go-sqlite3/issues/965 for more details exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible diff --git a/go.sum b/go.sum index 429d52747..748390242 100644 --- a/go.sum +++ b/go.sum @@ -130,7 +130,6 @@ github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bbalet/stopwords v1.0.0 h1:0TnGycCtY0zZi4ltKoOGRFIlZHv0WqpoIGUsObjztfo= github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc= -github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beevik/etree v1.5.0 h1:iaQZFSDS+3kYZiGoc9uKeOkUY3nYMXOKLl6KIJxiJWs= github.com/beevik/etree v1.5.0/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -237,7 +236,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo= github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4= github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c= @@ -335,8 +333,8 @@ github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc github.com/go-acme/lego/v4 v4.4.0/go.mod h1:l3+tFUFZb590dWcqhWZegynUthtaHJbG2fevUpoOOE0= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.4.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk= -github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= +github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= @@ -369,8 +367,8 @@ github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= -github.com/go-ldap/ldap/v3 v3.4.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU= -github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY= +github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU= +github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM= github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 h1:sfz1YppV05y4sYaW7kXZtrocU/+vimnIWt4cxAYh7+o= github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3/go.mod h1:ZXFhGda43Z2TVbfGZefXyMJzsDHhCh0go3bZUcwTx7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -574,8 +572,6 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= @@ -662,8 +658,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -707,8 +703,6 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -727,6 +721,8 @@ github.com/leonelquinteros/gotext v1.7.1 h1:/JNPeE3lY5JeVYv2+KBpz39994W3W9fmZCGq github.com/leonelquinteros/gotext v1.7.1/go.mod h1:I0WoFDn9u2D3VbPnnDPT8mzZu0iSXG8iih+AH2fHHqg= github.com/libregraph/idm v0.5.0 h1:tDMwKbAOZzdeDYMxVlY5PbSqRKO7dbAW9KT42A51WSk= github.com/libregraph/idm v0.5.0/go.mod h1:BGMwIQ/6orJSPVzJ1x6kgG2JyG9GY05YFmbsnaD80k0= +github.com/libregraph/lico v0.65.2-0.20250428103211-356e98f98457 h1:cwmUM+mSeqWYtZKAHn8QN7ns1nNf3Pc8nUfShka9+x0= +github.com/libregraph/lico v0.65.2-0.20250428103211-356e98f98457/go.mod h1:2s2UkO0pY7/k1UlenXwio1qenfHZ217Npx22YyZJfSA= github.com/libregraph/oidc-go v1.1.0 h1:RyudjL3UyQblqeBQI06W53PniWobqODeeyAy6v/HumA= github.com/libregraph/oidc-go v1.1.0/go.mod h1:qW9ubcXvZrfbbWZBaLMuk7bt5qAUMYyt9/NtXQt07Cw= github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE= @@ -895,7 +891,6 @@ github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0 github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -978,8 +973,6 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= @@ -988,8 +981,8 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= -github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys= -github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= +github.com/russellhaering/goxmldsig v1.5.0 h1:AU2UkkYIUOTyZRbe08XMThaOCelArgvNfYapcmSjBNw= +github.com/russellhaering/goxmldsig v1.5.0/go.mod h1:x98CjQNFJcWfMxeOrMnMKg70lvDP6tE0nTaeUnjXDmk= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1226,13 +1219,11 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1326,14 +1317,12 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1362,7 +1351,6 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1445,7 +1433,6 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= @@ -1459,7 +1446,6 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1477,7 +1463,6 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/ber.go b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go index 71b3c3ac2..f27229ed6 100644 --- a/vendor/github.com/go-asn1-ber/asn1-ber/ber.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go @@ -412,7 +412,7 @@ func readPacket(reader io.Reader) (*Packet, int, error) { p.Value = val } case TagRelativeOID: - oid, err := parseObjectIdentifier(content) + oid, err := parseRelativeObjectIdentifier(content) if err == nil { p.Value = OIDToString(oid) } @@ -560,16 +560,14 @@ func NewBoolean(classType Class, tagType Type, tag Tag, value bool, description // NewLDAPBoolean returns a RFC 4511-compliant Boolean packet. func NewLDAPBoolean(classType Class, tagType Type, tag Tag, value bool, description string) *Packet { - intValue := int64(0) - - if value { - intValue = 255 - } - p := Encode(classType, tagType, tag, nil, description) p.Value = value - p.Data.Write(encodeInteger(intValue)) + if value { + p.Data.Write([]byte{255}) + } else { + p.Data.Write([]byte{0}) + } return p } @@ -663,6 +661,25 @@ func NewOID(classType Class, tagType Type, tag Tag, value interface{}, descripti return p } +func NewRelativeOID(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet { + p := Encode(classType, tagType, tag, nil, description) + + switch v := value.(type) { + case string: + encoded, err := encodeRelativeOID(v) + if err != nil { + fmt.Printf("failed writing %v", err) + return nil + } + p.Value = v + p.Data.Write(encoded) + // TODO: support []int already ? + default: + panic(fmt.Sprintf("Invalid type %T, expected float{64|32}", v)) + } + return p +} + // encodeOID takes a string representation of an OID and returns its DER-encoded byte slice along with any error. func encodeOID(oidString string) ([]byte, error) { // Convert the string representation to an asn1.ObjectIdentifier @@ -688,6 +705,26 @@ func encodeOID(oidString string) ([]byte, error) { return encoded, nil } +func encodeRelativeOID(oidString string) ([]byte, error) { + parts := strings.Split(oidString, ".") + oid := make([]int, len(parts)) + for i, part := range parts { + var val int + if _, err := fmt.Sscanf(part, "%d", &val); err != nil { + return nil, fmt.Errorf("invalid RELATIVE OID part '%s': %w", part, err) + } + oid[i] = val + } + + encoded := make([]byte, 0) + + for i := 0; i < len(oid); i++ { + encoded = appendBase128Int(encoded, int64(oid[i])) + } + + return encoded, nil +} + func appendBase128Int(dst []byte, n int64) []byte { l := base128IntLength(n) @@ -772,6 +809,27 @@ func parseObjectIdentifier(bytes []byte) (s []int, err error) { return } +func parseRelativeObjectIdentifier(bytes []byte) (s []int, err error) { + if len(bytes) == 0 { + err = fmt.Errorf("zero length RELATIVE OBJECT IDENTIFIER") + return + } + + s = make([]int, len(bytes)+1) + + var v, offset int + i := 0 + for ; offset < len(bytes); i++ { + v, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + s[i] = v + } + s = s[0:i] + return +} + // parseBase128Int parses a base-128 encoded int from the given offset in the // given byte slice. It returns the value and the new offset. func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) { diff --git a/vendor/github.com/go-ldap/ldap/v3/bind.go b/vendor/github.com/go-ldap/ldap/v3/bind.go index a37f8e2c7..6cfd37ebe 100644 --- a/vendor/github.com/go-ldap/ldap/v3/bind.go +++ b/vendor/github.com/go-ldap/ldap/v3/bind.go @@ -3,15 +3,19 @@ package ldap import ( "bytes" "crypto/md5" + "encoding/binary" + "encoding/hex" enchex "encoding/hex" "errors" "fmt" "io/ioutil" "math/rand" "strings" + "unicode/utf16" "github.com/Azure/go-ntlmssp" ber "github.com/go-asn1-ber/asn1-ber" + "golang.org/x/crypto/md4" //nolint:staticcheck ) // SimpleBindRequest represents a username/password bind operation @@ -216,7 +220,7 @@ func (l *Conn) DigestMD5Bind(digestMD5BindRequest *DigestMD5BindRequest) (*Diges } } - if params != nil { + if len(params) > 0 { resp := computeResponse( params, "ldap/"+strings.ToLower(digestMD5BindRequest.Host), @@ -249,6 +253,34 @@ func (l *Conn) DigestMD5Bind(digestMD5BindRequest *DigestMD5BindRequest) (*Diges if err != nil { return nil, fmt.Errorf("read packet: %s", err) } + + if len(packet.Children) == 2 { + response := packet.Children[1] + if response == nil { + return result, GetLDAPError(packet) + } + if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 { + if ber.Type(response.Children[0].Tag) == ber.Type(ber.TagInteger) || ber.Type(response.Children[0].Tag) == ber.Type(ber.TagEnumerated) { + resultCode := uint16(response.Children[0].Value.(int64)) + if resultCode == 14 { + msgCtx, err := l.doRequest(digestMD5BindRequest) + if err != nil { + return nil, err + } + defer l.finishMessage(msgCtx) + packetResponse, ok := <-msgCtx.responses + if !ok { + return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) + } + packet, err = packetResponse.ReadPacket() + l.Debug.Printf("%d: got response %p", msgCtx.id, packet) + if err != nil { + return nil, fmt.Errorf("read packet: %s", err) + } + } + } + } + } } err = GetLDAPError(packet) @@ -406,17 +438,36 @@ type NTLMBindRequest struct { Hash string // Controls are optional controls to send with the bind request Controls []Control + // Negotiator allows to specify a custom NTLM negotiator. + Negotiator NTLMNegotiator } -func (req *NTLMBindRequest) appendTo(envelope *ber.Packet) error { +// NTLMNegotiator is an abstraction of an NTLM implementation that produces and +// processes NTLM binary tokens. +type NTLMNegotiator interface { + Negotiate(domain string, workstation string) ([]byte, error) + ChallengeResponse(challenge []byte, username string, hash string) ([]byte, error) +} + +func (req *NTLMBindRequest) appendTo(envelope *ber.Packet) (err error) { request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name")) + var negMessage []byte + // generate an NTLMSSP Negotiation message for the specified domain (it can be blank) - negMessage, err := ntlmssp.NewNegotiateMessage(req.Domain, "") - if err != nil { - return fmt.Errorf("err creating negmessage: %s", err) + switch { + case req.Negotiator == nil: + negMessage, err = ntlmssp.NewNegotiateMessage(req.Domain, "") + if err != nil { + return fmt.Errorf("create NTLM negotiate message: %s", err) + } + default: + negMessage, err = req.Negotiator.Negotiate(req.Domain, "") + if err != nil { + return fmt.Errorf("create NTLM negotiate message with custom negotiator: %s", err) + } } // append the generated NTLMSSP message as a TagEnumerated BER value @@ -514,18 +565,29 @@ func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindRes if ntlmsspChallenge != nil { var err error var responseMessage []byte - // generate a response message to the challenge with the given Username/Password if password is provided - if ntlmBindRequest.Hash != "" { + + switch { + case ntlmBindRequest.Hash == "" && ntlmBindRequest.Password == "" && !ntlmBindRequest.AllowEmptyPassword: + err = fmt.Errorf("need a password or hash to generate reply") + case ntlmBindRequest.Negotiator == nil && ntlmBindRequest.Hash != "": responseMessage, err = ntlmssp.ProcessChallengeWithHash(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Hash) - } else if ntlmBindRequest.Password != "" || ntlmBindRequest.AllowEmptyPassword { + case ntlmBindRequest.Negotiator == nil && (ntlmBindRequest.Password != "" || ntlmBindRequest.AllowEmptyPassword): + // generate a response message to the challenge with the given Username/Password if password is provided _, _, domainNeeded := ntlmssp.GetDomain(ntlmBindRequest.Username) responseMessage, err = ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password, domainNeeded) - } else { - err = fmt.Errorf("need a password or hash to generate reply") + default: + hash := ntlmBindRequest.Hash + if len(hash) == 0 { + hash = ntHash(ntlmBindRequest.Password) + } + + responseMessage, err = ntlmBindRequest.Negotiator.ChallengeResponse(ntlmsspChallenge, ntlmBindRequest.Username, hash) } + if err != nil { - return result, fmt.Errorf("parsing ntlm-challenge: %s", err) + return result, fmt.Errorf("process NTLM challenge: %s", err) } + packet = ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) @@ -559,6 +621,18 @@ func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindRes return result, err } +func ntHash(pass string) string { + runes := utf16.Encode([]rune(pass)) + + b := bytes.Buffer{} + _ = binary.Write(&b, binary.LittleEndian, &runes) + + hash := md4.New() + _, _ = hash.Write(b.Bytes()) + + return hex.EncodeToString(hash.Sum(nil)) +} + // GSSAPIClient interface is used as the client-side implementation for the // GSSAPI SASL mechanism. // Interface inspired by GSSAPIClient from golang.org/x/crypto/ssh @@ -577,6 +651,9 @@ type GSSAPIClient interface { // to InitSecContext via the token parameters. // See RFC 4752 section 3.1. InitSecContext(target string, token []byte) (outputToken []byte, needContinue bool, err error) + // InitSecContextWithOptions is the same as InitSecContext but allows for additional options to be passed to the context establishment. + // See RFC 4752 section 3.1. + InitSecContextWithOptions(target string, token []byte, options []int) (outputToken []byte, needContinue bool, err error) // NegotiateSaslAuth performs the last step of the Sasl handshake. // It takes a token, which, when unwrapped, describes the servers supported // security layers (first octet) and maximum receive buffer (remaining @@ -614,6 +691,11 @@ func (l *Conn) GSSAPIBind(client GSSAPIClient, servicePrincipal, authzid string) // GSSAPIBindRequest performs the GSSAPI SASL bind using the provided GSSAPI client. func (l *Conn) GSSAPIBindRequest(client GSSAPIClient, req *GSSAPIBindRequest) error { + return l.GSSAPIBindRequestWithAPOptions(client, req, []int{}) +} + +// GSSAPIBindRequest performs the GSSAPI SASL bind using the provided GSSAPI client. +func (l *Conn) GSSAPIBindRequestWithAPOptions(client GSSAPIClient, req *GSSAPIBindRequest, APOptions []int) error { //nolint:errcheck defer client.DeleteSecContext() @@ -624,7 +706,7 @@ func (l *Conn) GSSAPIBindRequest(client GSSAPIClient, req *GSSAPIBindRequest) er for { if needInit { // Establish secure context between client and server. - reqToken, needInit, err = client.InitSecContext(req.ServicePrincipalName, recvToken) + reqToken, needInit, err = client.InitSecContextWithOptions(req.ServicePrincipalName, recvToken, APOptions) if err != nil { return err } diff --git a/vendor/github.com/go-ldap/ldap/v3/control.go b/vendor/github.com/go-ldap/ldap/v3/control.go index ab75c3423..f1c2746ba 100644 --- a/vendor/github.com/go-ldap/ldap/v3/control.go +++ b/vendor/github.com/go-ldap/ldap/v3/control.go @@ -709,7 +709,7 @@ func (c *ControlDirSync) Encode() *ber.Packet { packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeDirSync, "Control Type ("+ControlTypeMap[ControlTypeDirSync]+")")) - packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) // must be true always + packet.AppendChild(ber.NewLDAPBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) // must be true always val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (DirSync)") seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "DirSync Control Value") @@ -755,7 +755,7 @@ func NewControlServerSideSorting(value *ber.Packet) (*ControlServerSideSorting, sequences := val[0].Children for i, sequence := range sequences { - sortKey := &SortKey{} + sortKey := new(SortKey) if len(sequence.Children) < 2 { return nil, fmt.Errorf("attributeType or matchingRule is missing from sequence %d", i) @@ -864,10 +864,14 @@ func (c ControlServerSideSortingCode) Valid() error { } func NewControlServerSideSortingResult(pkt *ber.Packet) (*ControlServerSideSortingResult, error) { - control := &ControlServerSideSortingResult{} + control := new(ControlServerSideSortingResult) if pkt == nil || len(pkt.Children) == 0 { - return nil, fmt.Errorf("bad packet") + // This is currently not compliant with the ServerSideSorting RFC (see https://datatracker.ietf.org/doc/html/rfc2891#section-1.2). + // but it's necessary because there seems to be a bug in the implementation of the popular OpenLDAP server. + // + // See: https://github.com/go-ldap/ldap/pull/546 + return control, nil } codeInt, err := ber.ParseInt64(pkt.Children[0].Data.Bytes()) @@ -875,8 +879,7 @@ func NewControlServerSideSortingResult(pkt *ber.Packet) (*ControlServerSideSorti return nil, err } - code := ControlServerSideSortingCode(codeInt) - if err := code.Valid(); err != nil { + if err = ControlServerSideSortingCode(codeInt).Valid(); err != nil { return nil, err } diff --git a/vendor/github.com/jonboulle/clockwork/README.md b/vendor/github.com/jonboulle/clockwork/README.md index cad608357..5e9d472ea 100644 --- a/vendor/github.com/jonboulle/clockwork/README.md +++ b/vendor/github.com/jonboulle/clockwork/README.md @@ -2,9 +2,9 @@ [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#utilities) -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/jonboulle/clockwork/CI?style=flat-square)](https://github.com/jonboulle/clockwork/actions?query=workflow%3ACI) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/jonboulle/clockwork/ci.yaml?style=flat-square)](https://github.com/jonboulle/clockwork/actions?query=workflow%3ACI) [![Go Report Card](https://goreportcard.com/badge/github.com/jonboulle/clockwork?style=flat-square)](https://goreportcard.com/report/github.com/jonboulle/clockwork) -![Go Version](https://img.shields.io/badge/go%20version-%3E=1.11-61CFDD.svg?style=flat-square) +![Go Version](https://img.shields.io/badge/go%20version-%3E=1.15-61CFDD.svg?style=flat-square) [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/jonboulle/clockwork) **A simple fake clock for Go.** @@ -36,6 +36,7 @@ Now you can easily test `myFunc` with a `FakeClock`: ```go func TestMyFunc(t *testing.T) { + ctx := context.Background() c := clockwork.NewFakeClock() // Start our sleepy function @@ -46,8 +47,12 @@ func TestMyFunc(t *testing.T) { wg.Done() }() - // Ensure we wait until myFunc is sleeping - c.BlockUntil(1) + // Ensure we wait until myFunc is waiting on the clock. + // Use a context to avoid blocking forever if something + // goes wrong. + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + c.BlockUntilContext(ctx, 1) assertState() diff --git a/vendor/github.com/jonboulle/clockwork/SECURITY.md b/vendor/github.com/jonboulle/clockwork/SECURITY.md new file mode 100644 index 000000000..0efcad9f6 --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it +privately. **Do not disclose it as a public issue.** This gives me time to work with you +to fix the issue before public exposure, reducing the chance that the exploit will be +used before a patch is released. + +You may submit the report in the following ways: + +- send an email to ???@???; and/or +- send a [private vulnerability report](https://github.com/jonboulle/clockwork/security/advisories/new) + +Please provide the following information in your report: + +- A description of the vulnerability and its impact +- How to reproduce the issue + +This project is maintained by a single maintainer on a reasonable-effort basis. As such, +please give me 90 days to work on a fix before public exposure. diff --git a/vendor/github.com/jonboulle/clockwork/clockwork.go b/vendor/github.com/jonboulle/clockwork/clockwork.go index 1018051f4..85a993467 100644 --- a/vendor/github.com/jonboulle/clockwork/clockwork.go +++ b/vendor/github.com/jonboulle/clockwork/clockwork.go @@ -1,30 +1,25 @@ +// Package clockwork contains a simple fake clock for Go. package clockwork import ( + "context" + "errors" + "slices" "sync" "time" ) -// Clock provides an interface that packages can use instead of directly -// using the time module, so that chronology-related behavior can be tested +// Clock provides an interface that packages can use instead of directly using +// the [time] module, so that chronology-related behavior can be tested. type Clock interface { After(d time.Duration) <-chan time.Time Sleep(d time.Duration) Now() time.Time Since(t time.Time) time.Duration + Until(t time.Time) time.Duration NewTicker(d time.Duration) Ticker -} - -// FakeClock provides an interface for a clock which can be -// manually advanced through time -type FakeClock interface { - Clock - // Advance advances the FakeClock to a new point in time, ensuring any existing - // sleepers are notified appropriately before returning - Advance(d time.Duration) - // BlockUntil will block until the FakeClock has the given number of - // sleepers (callers of Sleep or After) - BlockUntil(n int) + NewTimer(d time.Duration) Timer + AfterFunc(d time.Duration, f func()) Timer } // NewRealClock returns a Clock which simply delegates calls to the actual time @@ -33,21 +28,6 @@ func NewRealClock() Clock { return &realClock{} } -// NewFakeClock returns a FakeClock implementation which can be -// manually advanced through time for testing. The initial time of the -// FakeClock will be an arbitrary non-zero time. -func NewFakeClock() FakeClock { - // use a fixture that does not fulfill Time.IsZero() - return NewFakeClockAt(time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC)) -} - -// NewFakeClockAt returns a FakeClock initialised at the given time.Time. -func NewFakeClockAt(t time.Time) FakeClock { - return &fakeClock{ - time: t, - } -} - type realClock struct{} func (rc *realClock) After(d time.Duration) <-chan time.Time { @@ -66,130 +46,274 @@ func (rc *realClock) Since(t time.Time) time.Duration { return rc.Now().Sub(t) } -func (rc *realClock) NewTicker(d time.Duration) Ticker { - return &realTicker{time.NewTicker(d)} +func (rc *realClock) Until(t time.Time) time.Duration { + return t.Sub(rc.Now()) } -type fakeClock struct { - sleepers []*sleeper +func (rc *realClock) NewTicker(d time.Duration) Ticker { + return realTicker{time.NewTicker(d)} +} + +func (rc *realClock) NewTimer(d time.Duration) Timer { + return realTimer{time.NewTimer(d)} +} + +func (rc *realClock) AfterFunc(d time.Duration, f func()) Timer { + return realTimer{time.AfterFunc(d, f)} +} + +// FakeClock provides an interface for a clock which can be manually advanced +// through time. +// +// FakeClock maintains a list of "waiters," which consists of all callers +// waiting on the underlying clock (i.e. Tickers and Timers including callers of +// Sleep or After). Users can call BlockUntil to block until the clock has an +// expected number of waiters. +type FakeClock struct { + // l protects all attributes of the clock, including all attributes of all + // waiters and blockers. + l sync.RWMutex + waiters []expirer blockers []*blocker time time.Time - - l sync.RWMutex } -// sleeper represents a caller of After or Sleep -type sleeper struct { - until time.Time - done chan time.Time +// NewFakeClock returns a FakeClock implementation which can be +// manually advanced through time for testing. The initial time of the +// FakeClock will be the current system time. +// +// Tests that require a deterministic time must use NewFakeClockAt. +func NewFakeClock() *FakeClock { + return NewFakeClockAt(time.Now()) } -// blocker represents a caller of BlockUntil +// NewFakeClockAt returns a FakeClock initialised at the given time.Time. +func NewFakeClockAt(t time.Time) *FakeClock { + return &FakeClock{ + time: t, + } +} + +// blocker is a caller of BlockUntil. type blocker struct { count int - ch chan struct{} + + // ch is closed when the underlying clock has the specified number of blockers. + ch chan struct{} } -// After mimics time.After; it waits for the given duration to elapse on the +// expirer is a timer or ticker that expires at some point in the future. +type expirer interface { + // expire the expirer at the given time, returning the desired duration until + // the next expiration, if any. + expire(now time.Time) (next *time.Duration) + + // Get and set the expiration time. + expiration() time.Time + setExpiration(time.Time) +} + +// After mimics [time.After]; it waits for the given duration to elapse on the // fakeClock, then sends the current time on the returned channel. -func (fc *fakeClock) After(d time.Duration) <-chan time.Time { - fc.l.Lock() - defer fc.l.Unlock() - now := fc.time - done := make(chan time.Time, 1) - if d.Nanoseconds() <= 0 { - // special case - trigger immediately - done <- now - } else { - // otherwise, add to the set of sleepers - s := &sleeper{ - until: now.Add(d), - done: done, - } - fc.sleepers = append(fc.sleepers, s) - // and notify any blockers - fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers)) - } - return done +func (fc *FakeClock) After(d time.Duration) <-chan time.Time { + return fc.NewTimer(d).Chan() } -// notifyBlockers notifies all the blockers waiting until the -// given number of sleepers are waiting on the fakeClock. It -// returns an updated slice of blockers (i.e. those still waiting) -func notifyBlockers(blockers []*blocker, count int) (newBlockers []*blocker) { - for _, b := range blockers { - if b.count == count { - close(b.ch) - } else { - newBlockers = append(newBlockers, b) - } - } - return -} - -// Sleep blocks until the given duration has passed on the fakeClock -func (fc *fakeClock) Sleep(d time.Duration) { +// Sleep blocks until the given duration has passed on the fakeClock. +func (fc *FakeClock) Sleep(d time.Duration) { <-fc.After(d) } -// Time returns the current time of the fakeClock -func (fc *fakeClock) Now() time.Time { +// Now returns the current time of the fakeClock +func (fc *FakeClock) Now() time.Time { fc.l.RLock() - t := fc.time - fc.l.RUnlock() - return t + defer fc.l.RUnlock() + return fc.time } -// Since returns the duration that has passed since the given time on the fakeClock -func (fc *fakeClock) Since(t time.Time) time.Duration { +// Since returns the duration that has passed since the given time on the +// fakeClock. +func (fc *FakeClock) Since(t time.Time) time.Duration { return fc.Now().Sub(t) } -func (fc *fakeClock) NewTicker(d time.Duration) Ticker { - ft := &fakeTicker{ - c: make(chan time.Time, 1), - stop: make(chan bool, 1), - clock: fc, - period: d, +// Until returns the duration that has to pass from the given time on the fakeClock +// to reach the given time. +func (fc *FakeClock) Until(t time.Time) time.Duration { + return t.Sub(fc.Now()) +} + +// NewTicker returns a Ticker that will expire only after calls to +// FakeClock.Advance() have moved the clock past the given duration. +// +// The duration d must be greater than zero; if not, NewTicker will panic. +func (fc *FakeClock) NewTicker(d time.Duration) Ticker { + // Maintain parity with + // https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/time/tick.go;l=23-25 + if d <= 0 { + panic(errors.New("non-positive interval for NewTicker")) } - ft.runTickThread() + ft := newFakeTicker(fc, d) + fc.l.Lock() + defer fc.l.Unlock() + fc.setExpirer(ft, d) return ft } -// Advance advances fakeClock to a new point in time, ensuring channels from any -// previous invocations of After are notified appropriately before returning -func (fc *fakeClock) Advance(d time.Duration) { +// NewTimer returns a Timer that will fire only after calls to +// fakeClock.Advance() have moved the clock past the given duration. +func (fc *FakeClock) NewTimer(d time.Duration) Timer { + t, _ := fc.newTimer(d, nil) + return t +} + +// AfterFunc mimics [time.AfterFunc]; it returns a Timer that will invoke the +// given function only after calls to fakeClock.Advance() have moved the clock +// past the given duration. +func (fc *FakeClock) AfterFunc(d time.Duration, f func()) Timer { + t, _ := fc.newTimer(d, f) + return t +} + +// newTimer returns a new timer using an optional afterFunc and the time that +// timer expires. +func (fc *FakeClock) newTimer(d time.Duration, afterfunc func()) (*fakeTimer, time.Time) { + ft := newFakeTimer(fc, afterfunc) + fc.l.Lock() + defer fc.l.Unlock() + fc.setExpirer(ft, d) + return ft, ft.expiration() +} + +// newTimerAtTime is like newTimer, but uses a time instead of a duration. +// +// It is used to ensure FakeClock's lock is held constant through calling +// fc.After(t.Sub(fc.Now())). It should not be exposed externally. +func (fc *FakeClock) newTimerAtTime(t time.Time, afterfunc func()) *fakeTimer { + ft := newFakeTimer(fc, afterfunc) + fc.l.Lock() + defer fc.l.Unlock() + fc.setExpirer(ft, t.Sub(fc.time)) + return ft +} + +// Advance advances fakeClock to a new point in time, ensuring waiters and +// blockers are notified appropriately before returning. +func (fc *FakeClock) Advance(d time.Duration) { fc.l.Lock() defer fc.l.Unlock() end := fc.time.Add(d) - var newSleepers []*sleeper - for _, s := range fc.sleepers { - if end.Sub(s.until) >= 0 { - s.done <- end - } else { - newSleepers = append(newSleepers, s) + // Expire the earliest waiter until the earliest waiter's expiration is after + // end. + // + // We don't iterate because the callback of the waiter might register a new + // waiter, so the list of waiters might change as we execute this. + for len(fc.waiters) > 0 && !end.Before(fc.waiters[0].expiration()) { + w := fc.waiters[0] + fc.waiters = fc.waiters[1:] + + // Use the waiter's expiration as the current time for this expiration. + now := w.expiration() + fc.time = now + if d := w.expire(now); d != nil { + // Set the new expiration if needed. + fc.setExpirer(w, *d) } } - fc.sleepers = newSleepers - fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers)) fc.time = end } -// BlockUntil will block until the fakeClock has the given number of sleepers -// (callers of Sleep or After) -func (fc *fakeClock) BlockUntil(n int) { - fc.l.Lock() - // Fast path: current number of sleepers is what we're looking for - if len(fc.sleepers) == n { - fc.l.Unlock() - return +// BlockUntil blocks until the FakeClock has the given number of waiters. +// +// Prefer BlockUntilContext in new code, which offers context cancellation to +// prevent deadlock. +// +// Deprecated: New code should prefer BlockUntilContext. +func (fc *FakeClock) BlockUntil(n int) { + fc.BlockUntilContext(context.TODO(), n) +} + +// BlockUntilContext blocks until the fakeClock has the given number of waiters +// or the context is cancelled. +func (fc *FakeClock) BlockUntilContext(ctx context.Context, n int) error { + b := fc.newBlocker(n) + if b == nil { + return nil } - // Otherwise, set up a new blocker + + select { + case <-b.ch: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} + +func (fc *FakeClock) newBlocker(n int) *blocker { + fc.l.Lock() + defer fc.l.Unlock() + // Fast path: we already have >= n waiters. + if len(fc.waiters) >= n { + return nil + } + // Set up a new blocker to wait for more waiters. b := &blocker{ count: n, ch: make(chan struct{}), } fc.blockers = append(fc.blockers, b) - fc.l.Unlock() - <-b.ch + return b +} + +// stop stops an expirer, returning true if the expirer was stopped. +func (fc *FakeClock) stop(e expirer) bool { + fc.l.Lock() + defer fc.l.Unlock() + return fc.stopExpirer(e) +} + +// stopExpirer stops an expirer, returning true if the expirer was stopped. +// +// The caller must hold fc.l. +func (fc *FakeClock) stopExpirer(e expirer) bool { + idx := slices.Index(fc.waiters, e) + if idx == -1 { + return false + } + // Remove element, maintaining order, setting inaccessible elements to nil so + // they can be garbage collected. + copy(fc.waiters[idx:], fc.waiters[idx+1:]) + fc.waiters[len(fc.waiters)-1] = nil + fc.waiters = fc.waiters[:len(fc.waiters)-1] + return true +} + +// setExpirer sets an expirer to expire at a future point in time. +// +// The caller must hold fc.l. +func (fc *FakeClock) setExpirer(e expirer, d time.Duration) { + if d.Nanoseconds() <= 0 { + // Special case for timers with duration <= 0: trigger immediately, never + // reset. + // + // Tickers never get here, they panic if d is < 0. + e.expire(fc.time) + return + } + // Add the expirer to the set of waiters and notify any blockers. + e.setExpiration(fc.time.Add(d)) + fc.waiters = append(fc.waiters, e) + slices.SortFunc(fc.waiters, func(a, b expirer) int { + return a.expiration().Compare(b.expiration()) + }) + + // Notify blockers of our new waiter. + count := len(fc.waiters) + fc.blockers = slices.DeleteFunc(fc.blockers, func(b *blocker) bool { + if b.count <= count { + close(b.ch) + return true + } + return false + }) } diff --git a/vendor/github.com/jonboulle/clockwork/context.go b/vendor/github.com/jonboulle/clockwork/context.go new file mode 100644 index 000000000..59242619e --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/context.go @@ -0,0 +1,169 @@ +package clockwork + +import ( + "context" + "fmt" + "sync" + "time" +) + +// contextKey is private to this package so we can ensure uniqueness here. This +// type identifies context values provided by this package. +type contextKey string + +// keyClock provides a clock for injecting during tests. If absent, a real clock +// should be used. +var keyClock = contextKey("clock") // clockwork.Clock + +// AddToContext creates a derived context that references the specified clock. +// +// Be aware this doesn't change the behavior of standard library functions, such +// as [context.WithTimeout] or [context.WithDeadline]. For this reason, users +// should prefer passing explicit [clockwork.Clock] variables rather can passing +// the clock via the context. +func AddToContext(ctx context.Context, clock Clock) context.Context { + return context.WithValue(ctx, keyClock, clock) +} + +// FromContext extracts a clock from the context. If not present, a real clock +// is returned. +func FromContext(ctx context.Context) Clock { + if clock, ok := ctx.Value(keyClock).(Clock); ok { + return clock + } + return NewRealClock() +} + +// ErrFakeClockDeadlineExceeded is the error returned by [context.Context] when +// the deadline passes on a context which uses a [FakeClock]. +// +// It wraps a [context.DeadlineExceeded] error, i.e.: +// +// // The following is true for any Context whose deadline has been exceeded, +// // including contexts made with clockwork.WithDeadline or clockwork.WithTimeout. +// +// errors.Is(ctx.Err(), context.DeadlineExceeded) +// +// // The following can only be true for contexts made +// // with clockwork.WithDeadline or clockwork.WithTimeout. +// +// errors.Is(ctx.Err(), clockwork.ErrFakeClockDeadlineExceeded) +var ErrFakeClockDeadlineExceeded error = fmt.Errorf("clockwork.FakeClock: %w", context.DeadlineExceeded) + +// WithDeadline returns a context with a deadline based on a [FakeClock]. +// +// The returned context ignores parent cancelation if the parent was cancelled +// with a [context.DeadlineExceeded] error. Any other error returned by the +// parent is treated normally, cancelling the returned context. +// +// If the parent is cancelled with a [context.DeadlineExceeded] error, the only +// way to then cancel the returned context is by calling the returned +// context.CancelFunc. +func WithDeadline(parent context.Context, clock Clock, t time.Time) (context.Context, context.CancelFunc) { + if fc, ok := clock.(*FakeClock); ok { + return newFakeClockContext(parent, t, fc.newTimerAtTime(t, nil).Chan()) + } + return context.WithDeadline(parent, t) +} + +// WithTimeout returns a context with a timeout based on a [FakeClock]. +// +// The returned context follows the same behaviors as [WithDeadline]. +func WithTimeout(parent context.Context, clock Clock, d time.Duration) (context.Context, context.CancelFunc) { + if fc, ok := clock.(*FakeClock); ok { + t, deadline := fc.newTimer(d, nil) + return newFakeClockContext(parent, deadline, t.Chan()) + } + return context.WithTimeout(parent, d) +} + +// fakeClockContext implements context.Context, using a fake clock for its +// deadline. +// +// It ignores parent cancellation if the parent is cancelled with +// context.DeadlineExceeded. +type fakeClockContext struct { + parent context.Context + deadline time.Time // The user-facing deadline based on the fake clock's time. + + // Tracks timeout/deadline cancellation. + timerDone <-chan time.Time + + // Tracks manual calls to the cancel function. + cancel func() // Closes cancelCalled wrapped in a sync.Once. + cancelCalled chan struct{} + + // The user-facing data from the context.Context interface. + ctxDone chan struct{} // Returned by Done(). + err error // nil until ctxDone is ready to be closed. +} + +func newFakeClockContext(parent context.Context, deadline time.Time, timer <-chan time.Time) (context.Context, context.CancelFunc) { + cancelCalled := make(chan struct{}) + ctx := &fakeClockContext{ + parent: parent, + deadline: deadline, + timerDone: timer, + cancelCalled: cancelCalled, + ctxDone: make(chan struct{}), + cancel: sync.OnceFunc(func() { + close(cancelCalled) + }), + } + ready := make(chan struct{}, 1) + go ctx.runCancel(ready) + <-ready // Wait until the cancellation goroutine is running. + return ctx, ctx.cancel +} + +func (c *fakeClockContext) Deadline() (time.Time, bool) { + return c.deadline, true +} + +func (c *fakeClockContext) Done() <-chan struct{} { + return c.ctxDone +} + +func (c *fakeClockContext) Err() error { + <-c.Done() // Don't return the error before it is ready. + return c.err +} + +func (c *fakeClockContext) Value(key any) any { + return c.parent.Value(key) +} + +// runCancel runs the fakeClockContext's cancel goroutine and returns the +// fakeClockContext's cancel function. +// +// fakeClockContext is then cancelled when any of the following occur: +// +// - The fakeClockContext.done channel is closed by its timer. +// - The returned CancelFunc is executed. +// - The fakeClockContext's parent context is cancelled with an error other +// than context.DeadlineExceeded. +func (c *fakeClockContext) runCancel(ready chan struct{}) { + parentDone := c.parent.Done() + + // Close ready when done, just in case the ready signal races with other + // branches of our select statement below. + defer close(ready) + + for c.err == nil { + select { + case <-c.timerDone: + c.err = ErrFakeClockDeadlineExceeded + case <-c.cancelCalled: + c.err = context.Canceled + case <-parentDone: + c.err = c.parent.Err() + + case ready <- struct{}{}: + // Signals the cancellation goroutine has begun, in an attempt to minimize + // race conditions related to goroutine startup time. + ready = nil // This case statement can only fire once. + } + } + close(c.ctxDone) + return +} diff --git a/vendor/github.com/jonboulle/clockwork/ticker.go b/vendor/github.com/jonboulle/clockwork/ticker.go index 32b5d01e7..aa56952e7 100644 --- a/vendor/github.com/jonboulle/clockwork/ticker.go +++ b/vendor/github.com/jonboulle/clockwork/ticker.go @@ -1,72 +1,71 @@ package clockwork -import ( - "time" -) +import "time" -// Ticker provides an interface which can be used instead of directly -// using the ticker within the time module. The real-time ticker t -// provides ticks through t.C which becomes now t.Chan() to make -// this channel requirement definable in this interface. +// Ticker provides an interface which can be used instead of directly using +// [time.Ticker]. The real-time ticker t provides ticks through t.C which +// becomes t.Chan() to make this channel requirement definable in this +// interface. type Ticker interface { Chan() <-chan time.Time + Reset(d time.Duration) Stop() } type realTicker struct{ *time.Ticker } -func (rt *realTicker) Chan() <-chan time.Time { - return rt.C +func (r realTicker) Chan() <-chan time.Time { + return r.C } type fakeTicker struct { - c chan time.Time - stop chan bool - clock FakeClock - period time.Duration + // The channel associated with the firer, used to send expiration times. + c chan time.Time + + // The time when the ticker expires. Only meaningful if the ticker is currently + // one of a FakeClock's waiters. + exp time.Time + + // reset and stop provide the implementation of the respective exported + // functions. + reset func(d time.Duration) + stop func() + + // The duration of the ticker. + d time.Duration } -func (ft *fakeTicker) Chan() <-chan time.Time { - return ft.c +func newFakeTicker(fc *FakeClock, d time.Duration) *fakeTicker { + var ft *fakeTicker + ft = &fakeTicker{ + c: make(chan time.Time, 1), + d: d, + reset: func(d time.Duration) { + fc.l.Lock() + defer fc.l.Unlock() + ft.d = d + fc.setExpirer(ft, d) + }, + stop: func() { fc.stop(ft) }, + } + return ft } -func (ft *fakeTicker) Stop() { - ft.stop <- true +func (f *fakeTicker) Chan() <-chan time.Time { return f.c } + +func (f *fakeTicker) Reset(d time.Duration) { f.reset(d) } + +func (f *fakeTicker) Stop() { f.stop() } + +func (f *fakeTicker) expire(now time.Time) *time.Duration { + // Never block on expiration. + select { + case f.c <- now: + default: + } + return &f.d } -// runTickThread initializes a background goroutine to send the tick time to the ticker channel -// after every period. Tick events are discarded if the underlying ticker channel does not have -// enough capacity. -func (ft *fakeTicker) runTickThread() { - nextTick := ft.clock.Now().Add(ft.period) - next := ft.clock.After(ft.period) - go func() { - for { - select { - case <-ft.stop: - return - case <-next: - // We send the time that the tick was supposed to occur at. - tick := nextTick - // Before sending the tick, we'll compute the next tick time and star the clock.After call. - now := ft.clock.Now() - // First, figure out how many periods there have been between "now" and the time we were - // supposed to have trigged, then advance over all of those. - skipTicks := (now.Sub(tick) + ft.period - 1) / ft.period - nextTick = nextTick.Add(skipTicks * ft.period) - // Now, keep advancing until we are past now. This should happen at most once. - for !nextTick.After(now) { - nextTick = nextTick.Add(ft.period) - } - // Figure out how long between now and the next scheduled tick, then wait that long. - remaining := nextTick.Sub(now) - next = ft.clock.After(remaining) - // Finally, we can actually send the tick. - select { - case ft.c <- tick: - default: - } - } - } - }() -} +func (f *fakeTicker) expiration() time.Time { return f.exp } + +func (f *fakeTicker) setExpiration(t time.Time) { f.exp = t } diff --git a/vendor/github.com/jonboulle/clockwork/timer.go b/vendor/github.com/jonboulle/clockwork/timer.go new file mode 100644 index 000000000..e7e1d4001 --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/timer.go @@ -0,0 +1,79 @@ +package clockwork + +import "time" + +// Timer provides an interface which can be used instead of directly using +// [time.Timer]. The real-time timer t provides events through t.C which becomes +// t.Chan() to make this channel requirement definable in this interface. +type Timer interface { + Chan() <-chan time.Time + Reset(d time.Duration) bool + Stop() bool +} + +type realTimer struct{ *time.Timer } + +func (r realTimer) Chan() <-chan time.Time { + return r.C +} + +type fakeTimer struct { + // The channel associated with the firer, used to send expiration times. + c chan time.Time + + // The time when the firer expires. Only meaningful if the firer is currently + // one of a FakeClock's waiters. + exp time.Time + + // reset and stop provide the implementation of the respective exported + // functions. + reset func(d time.Duration) bool + stop func() bool + + // If present when the timer fires, the timer calls afterFunc in its own + // goroutine rather than sending the time on Chan(). + afterFunc func() +} + +func newFakeTimer(fc *FakeClock, afterfunc func()) *fakeTimer { + var ft *fakeTimer + ft = &fakeTimer{ + c: make(chan time.Time, 1), + reset: func(d time.Duration) bool { + fc.l.Lock() + defer fc.l.Unlock() + // fc.l must be held across the calls to stopExpirer & setExpirer. + stopped := fc.stopExpirer(ft) + fc.setExpirer(ft, d) + return stopped + }, + stop: func() bool { return fc.stop(ft) }, + + afterFunc: afterfunc, + } + return ft +} + +func (f *fakeTimer) Chan() <-chan time.Time { return f.c } + +func (f *fakeTimer) Reset(d time.Duration) bool { return f.reset(d) } + +func (f *fakeTimer) Stop() bool { return f.stop() } + +func (f *fakeTimer) expire(now time.Time) *time.Duration { + if f.afterFunc != nil { + go f.afterFunc() + return nil + } + + // Never block on expiration. + select { + case f.c <- now: + default: + } + return nil +} + +func (f *fakeTimer) expiration() time.Time { return f.exp } + +func (f *fakeTimer) setExpiration(t time.Time) { f.exp = t } diff --git a/vendor/github.com/libregraph/lico/identifier/package.json b/vendor/github.com/libregraph/lico/identifier/package.json index 7400e18a6..4254aacfe 100644 --- a/vendor/github.com/libregraph/lico/identifier/package.json +++ b/vendor/github.com/libregraph/lico/identifier/package.json @@ -65,7 +65,7 @@ "source-map-explorer": "^2.5.3", "terser": "^5.30.4", "typescript": "^5.2.2", - "vite": "^4.5.2", + "vite": "^4.5.13", "vite-plugin-checker": "^0.6.2", "vite-plugin-eslint": "^1.8.1", "vitest": "^0.34.6" diff --git a/vendor/github.com/russellhaering/goxmldsig/.travis.yml b/vendor/github.com/russellhaering/goxmldsig/.travis.yml deleted file mode 100644 index 8ad33f7db..000000000 --- a/vendor/github.com/russellhaering/goxmldsig/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -arch: - - amd64 - - ppc64le - -language: go - -go: - - "1.14.x" - - "1.15.x" - - "1.17.x" - - master diff --git a/vendor/github.com/russellhaering/goxmldsig/SECURITY.md b/vendor/github.com/russellhaering/goxmldsig/SECURITY.md new file mode 100644 index 000000000..f1442253d --- /dev/null +++ b/vendor/github.com/russellhaering/goxmldsig/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Security vulnerabilities can be reported using GitHub's [private vulnerability reporting tool](https://github.com/russellhaering/goxmldsig/security/advisories/new). diff --git a/vendor/github.com/russellhaering/goxmldsig/validate.go b/vendor/github.com/russellhaering/goxmldsig/validate.go index 3d0fc9739..e64891ccc 100644 --- a/vendor/github.com/russellhaering/goxmldsig/validate.go +++ b/vendor/github.com/russellhaering/goxmldsig/validate.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/x509" "encoding/base64" + "encoding/xml" "errors" "fmt" "regexp" @@ -169,6 +170,7 @@ func (ctx *ValidationContext) transform( return el, canonicalizer, nil } +// deprecated func (ctx *ValidationContext) digest(el *etree.Element, digestAlgorithmId string, canonicalizer Canonicalizer) ([]byte, error) { data, err := canonicalizer.Canonicalize(el) if err != nil { @@ -189,6 +191,33 @@ func (ctx *ValidationContext) digest(el *etree.Element, digestAlgorithmId string return hash.Sum(nil), nil } +func (ctx *ValidationContext) getCanonicalSignedInfo(sig *types.Signature) ([]byte, error) { + signatureElement := sig.UnderlyingElement() + + nsCtx, err := etreeutils.NSBuildParentContext(signatureElement) + if err != nil { + return nil, err + } + + signedInfo, err := etreeutils.NSFindOneChildCtx(nsCtx, signatureElement, Namespace, SignedInfoTag) + if err != nil { + return nil, err + } + + if signedInfo == nil { + return nil, errors.New("Missing SignedInfo") + } + + // Canonicalize the xml + canonical, err := canonicalSerialize(signedInfo) + if err != nil { + return nil, err + } + + return canonical, nil +} + +// deprecated func (ctx *ValidationContext) verifySignedInfo(sig *types.Signature, canonicalizer Canonicalizer, signatureMethodId string, cert *x509.Certificate, decodedSignature []byte) error { signatureElement := sig.UnderlyingElement() @@ -226,44 +255,24 @@ func (ctx *ValidationContext) verifySignedInfo(sig *types.Signature, canonicaliz } func (ctx *ValidationContext) validateSignature(el *etree.Element, sig *types.Signature, cert *x509.Certificate) (*etree.Element, error) { - idAttrEl := el.SelectAttr(ctx.IdAttribute) - idAttr := "" - if idAttrEl != nil { - idAttr = idAttrEl.Value - } - var ref *types.Reference + // Actually verify the 'SignedInfo' was signed by a trusted source + signatureMethod := sig.SignedInfo.SignatureMethod.Algorithm - // Find the first reference which references the top-level element - for _, _ref := range sig.SignedInfo.References { - if _ref.URI == "" || _ref.URI[1:] == idAttr { - ref = &_ref - } - } - - // Perform all transformations listed in the 'SignedInfo' - // Basically, this means removing the 'SignedInfo' - transformed, canonicalizer, err := ctx.transform(el, sig, ref) + canonicalSignedInfoBytes, err := ctx.getCanonicalSignedInfo(sig) if err != nil { - return nil, err + return nil, errors.New("Could not obtain canonical signed info bytes") } - digestAlgorithm := ref.DigestAlgo.Algorithm - - // Digest the transformed XML and compare it to the 'DigestValue' from the 'SignedInfo' - digest, err := ctx.digest(transformed, digestAlgorithm, canonicalizer) - if err != nil { - return nil, err + if canonicalSignedInfoBytes == nil { + return nil, errors.New("Missing SignedInfo") } - decodedDigestValue, err := base64.StdEncoding.DecodeString(ref.DigestValue) - if err != nil { - return nil, err + algo, ok := x509SignatureAlgorithmByIdentifier[signatureMethod] + if !ok { + return nil, errors.New("Unknown signature method: " + signatureMethod) } - if !bytes.Equal(digest, decodedDigestValue) { - return nil, errors.New("Signature could not be verified") - } if sig.SignatureValue == nil { return nil, errors.New("Signature could not be verified") } @@ -274,14 +283,92 @@ func (ctx *ValidationContext) validateSignature(el *etree.Element, sig *types.Si return nil, errors.New("Could not decode signature") } - // Actually verify the 'SignedInfo' was signed by a trusted source - signatureMethod := sig.SignedInfo.SignatureMethod.Algorithm - err = ctx.verifySignedInfo(sig, canonicalizer, signatureMethod, cert, decodedSignature) + err = cert.CheckSignature(algo, canonicalSignedInfoBytes, decodedSignature) if err != nil { return nil, err } - return transformed, nil + // only use the verified canonicalSignedInfoBytes + // unmarshal canonicalSignedInfoBytes into a new SignedInfo type + // to obtain the reference + signedInfo := &types.SignedInfo{} + err = xml.Unmarshal(canonicalSignedInfoBytes, signedInfo) + if err != nil { + return nil, err + } + + idAttrEl := el.SelectAttr(ctx.IdAttribute) + idAttr := "" + if idAttrEl != nil { + idAttr = idAttrEl.Value + } + + var ref *types.Reference + + // Find the first reference which references the top-level element + for _, _ref := range signedInfo.References { + if _ref.URI == "" || _ref.URI[1:] == idAttr { + ref = &_ref + } + } + + // prevents null pointer deref + if ref == nil { + return nil, errors.New("Missing reference") + } + + digestAlgorithmId := ref.DigestAlgo.Algorithm + signedDigestValue, err := base64.StdEncoding.DecodeString(ref.DigestValue) + if err != nil { + return nil, err + } + + // Perform all transformations listed in the 'SignedInfo' + // Basically, this means removing the 'SignedInfo' + transformed, canonicalizer, err := ctx.transform(el, sig, ref) + if err != nil { + return nil, err + } + + referencedBytes, err := canonicalizer.Canonicalize(transformed) + if err != nil { + return nil, err + } + + // use a known digest hashing algorithm + hashAlgorithm, ok := digestAlgorithmsByIdentifier[digestAlgorithmId] + if !ok { + return nil, errors.New("Unknown digest algorithm: " + digestAlgorithmId) + } + + hash := hashAlgorithm.New() + _, err = hash.Write(referencedBytes) + if err != nil { + return nil, err + } + + computedDigest := hash.Sum(nil) + /* Digest the transformed XML and compare it to the 'DigestValue' from the 'SignedInfo' + digest, err := ctx.digest(transformed, digestAlgorithm, canonicalizer) + */ + + if !bytes.Equal(computedDigest, signedDigestValue) { + return nil, errors.New("Signature could not be verified") + } + + if !(len(computedDigest) >= 20) { + return nil, errors.New("Computed digest is less than 20 something went wrong") + } + + // now only the referencedBytes is verified, + // unmarshal into new etree + doc := etree.NewDocument() + err = doc.ReadFromBytes(referencedBytes) + if err != nil { + return nil, err + } + + return doc.Root(), nil } func contains(roots []*x509.Certificate, cert *x509.Certificate) bool { @@ -425,7 +512,7 @@ func (ctx *ValidationContext) verifyCertificate(sig *types.Signature) (*x509.Cer return nil, err } - var cert *x509.Certificate + var untrustedCert *x509.Certificate if sig.KeyInfo != nil { // If the Signature includes KeyInfo, extract the certificate from there @@ -439,29 +526,40 @@ func (ctx *ValidationContext) verifyCertificate(sig *types.Signature) (*x509.Cer return nil, errors.New("Failed to parse certificate") } - cert, err = x509.ParseCertificate(certData) + untrustedCert, err = x509.ParseCertificate(certData) if err != nil { return nil, err } } else { // If the Signature doesn't have KeyInfo, Use the root certificate if there is only one if len(roots) == 1 { - cert = roots[0] + untrustedCert = roots[0] } else { return nil, errors.New("Missing x509 Element") } } - // Verify that the certificate is one we trust - if !contains(roots, cert) { - return nil, errors.New("Could not verify certificate against trusted certs") + rootIdx := -1 + for i, root := range roots { + if root.Equal(untrustedCert) { + rootIdx = i + } } - if now.Before(cert.NotBefore) || now.After(cert.NotAfter) { + if rootIdx == -1 { + return nil, errors.New("Could not verify certificate against trusted certs") + } + var trustedCert *x509.Certificate + + trustedCert = roots[rootIdx] + + // Verify that the certificate is one we trust + + if now.Before(trustedCert.NotBefore) || now.After(trustedCert.NotAfter) { return nil, errors.New("Cert is not valid at this time") } - return cert, nil + return trustedCert, nil } // Validate verifies that the passed element contains a valid enveloped signature @@ -475,6 +573,7 @@ func (ctx *ValidationContext) Validate(el *etree.Element) (*etree.Element, error return nil, err } + // function to get the trusted certificate cert, err := ctx.verifyCertificate(sig) if err != nil { return nil, err diff --git a/vendor/modules.txt b/vendor/modules.txt index 2d9bc941e..787d80910 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -433,7 +433,7 @@ github.com/ggwhite/go-masker ## explicit; go 1.15 github.com/go-acme/lego/v4/acme github.com/go-acme/lego/v4/challenge -# github.com/go-asn1-ber/asn1-ber v1.5.7 +# github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 ## explicit; go 1.13 github.com/go-asn1-ber/asn1-ber # github.com/go-chi/chi/v5 v5.2.1 @@ -523,8 +523,8 @@ github.com/go-jose/go-jose/v4/json ## explicit; go 1.17 github.com/go-kit/log github.com/go-kit/log/level -# github.com/go-ldap/ldap/v3 v3.4.10 -## explicit; go 1.14 +# github.com/go-ldap/ldap/v3 v3.4.11 +## explicit; go 1.23.0 github.com/go-ldap/ldap/v3 # github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 ## explicit; go 1.14 @@ -814,8 +814,8 @@ github.com/jinzhu/now # github.com/jmespath/go-jmespath v0.4.0 ## explicit; go 1.14 github.com/jmespath/go-jmespath -# github.com/jonboulle/clockwork v0.2.2 -## explicit; go 1.13 +# github.com/jonboulle/clockwork v0.5.0 +## explicit; go 1.21 github.com/jonboulle/clockwork # github.com/json-iterator/go v1.1.12 ## explicit; go 1.12 @@ -870,8 +870,8 @@ github.com/libregraph/idm/server github.com/libregraph/idm/server/handler github.com/libregraph/idm/server/handler/boltdb github.com/libregraph/idm/server/handler/ldif -# github.com/libregraph/lico v0.65.1 => github.com/dragonchaser/lico v0.0.0-20250416141658-4d60b0ff2e7d -## explicit; go 1.18 +# github.com/libregraph/lico v0.65.2-0.20250428103211-356e98f98457 +## explicit; go 1.23.0 github.com/libregraph/lico github.com/libregraph/lico/bootstrap github.com/libregraph/lico/bootstrap/backends/guest @@ -1702,8 +1702,8 @@ github.com/rs/zerolog/internal/cbor github.com/rs/zerolog/internal/json github.com/rs/zerolog/log github.com/rs/zerolog/pkgerrors -# github.com/russellhaering/goxmldsig v1.4.0 -## explicit; go 1.15 +# github.com/russellhaering/goxmldsig v1.5.0 +## explicit; go 1.21.0 github.com/russellhaering/goxmldsig github.com/russellhaering/goxmldsig/etreeutils github.com/russellhaering/goxmldsig/types @@ -2434,4 +2434,3 @@ stash.kopano.io/kgol/rndm # github.com/unrolled/secure => github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c # github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90 # go-micro.dev/v4 => github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3 -# github.com/libregraph/lico => github.com/dragonchaser/lico v0.0.0-20250416141658-4d60b0ff2e7d