Merge branch 'main' into dependabot/go_modules/src/github.com/volcengine/volcengine-go-sdk-1.1.17
Code scanning - action / CodeQL-Build (push) Has been cancelled Details
CI / UTTEST (push) Has been cancelled Details
CI / APITEST_DB (push) Has been cancelled Details
CI / APITEST_DB_PROXY_CACHE (push) Has been cancelled Details
CI / APITEST_LDAP (push) Has been cancelled Details
CI / OFFLINE (push) Has been cancelled Details
CI / UI_UT (push) Has been cancelled Details

This commit is contained in:
miner 2025-06-20 17:20:53 +08:00 committed by GitHub
commit 579f14bb29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
85 changed files with 314 additions and 309 deletions

View File

@ -27,7 +27,7 @@ func TestMaxOpenConns(t *testing.T) {
queryNum := 200
results := make([]bool, queryNum)
for i := 0; i < queryNum; i++ {
for i := range queryNum {
wg.Add(1)
go func(i int) {
defer wg.Done()

View File

@ -142,7 +142,7 @@ func ArrayEqual(arrayA, arrayB []int) bool {
return false
}
size := len(arrayA)
for i := 0; i < size; i++ {
for i := range size {
if arrayA[i] != arrayB[i] {
return false
}

View File

@ -119,7 +119,7 @@ func BenchmarkProjectEvaluator(b *testing.B) {
resource := NewNamespace(public.ProjectID).Resource(rbac.ResourceRepository)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
evaluator.HasPermission(context.TODO(), resource, rbac.ActionPull)
}
}

View File

@ -75,7 +75,7 @@ func GenerateRandomStringWithLen(length int) string {
if err != nil {
log.Warningf("Error reading random bytes: %v", err)
}
for i := 0; i < length; i++ {
for i := range length {
result[i] = chars[int(result[i])%l]
}
return string(result)
@ -337,13 +337,3 @@ func MostMatchSorter(a, b string, matchWord string) bool {
func IsLocalPath(path string) bool {
return len(path) == 0 || (strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "//"))
}
// StringInSlice check if the string is in the slice
func StringInSlice(str string, slice []string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}

View File

@ -66,8 +66,7 @@ func parseV1alpha1SkipList(artifact *artifact.Artifact, manifest *v1.Manifest) {
skipListAnnotationKey := fmt.Sprintf("%s.%s.%s", AnnotationPrefix, V1alpha1, SkipList)
skipList, ok := manifest.Config.Annotations[skipListAnnotationKey]
if ok {
skipKeyList := strings.Split(skipList, ",")
for _, skipKey := range skipKeyList {
for skipKey := range strings.SplitSeq(skipList, ",") {
delete(metadata, skipKey)
}
artifact.ExtraAttrs = metadata

View File

@ -156,7 +156,7 @@ func TestAddNode(t *testing.T) {
// Verify the path exists.
current := root
parts := filepath.Clean(tt.path)
for _, part := range strings.Split(parts, string(filepath.Separator)) {
for part := range strings.SplitSeq(parts, string(filepath.Separator)) {
if part == "" {
continue
}

View File

@ -232,7 +232,7 @@ func (suite *ControllerTestSuite) TestGet() {
func (suite *ControllerTestSuite) TestSync() {
var references []distribution.Descriptor
for i := 0; i < 5; i++ {
for i := range 5 {
references = append(references, distribution.Descriptor{
MediaType: fmt.Sprintf("media type %d", i),
Digest: suite.Digest(),

View File

@ -206,7 +206,7 @@ func maxValueLimitedByLength(length int) int64 {
var value int64
// the times for multiple, should *10 for every time
times := 1
for i := 0; i < length; i++ {
for range length {
value = value + int64(9*times)
times = times * 10
}

View File

@ -129,7 +129,7 @@ func constructScanImagePayload(ctx context.Context, event *event.ScanImageEvent,
// Wait for reasonable time to make sure the report is ready
// Interval=500ms and total time = 5s
// If the report is still not ready in the total time, then failed at then
for i := 0; i < 10; i++ {
for range 10 {
// First check in case it is ready
if re, err := scan.DefaultController.GetReport(ctx, art, []string{v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport}); err == nil {
if len(re) > 0 && len(re[0].Report) > 0 {

View File

@ -48,7 +48,7 @@ func (c *controller) GetHealth(_ context.Context) *OverallHealthStatus {
for name, checker := range registry {
go check(name, checker, timeout, ch)
}
for i := 0; i < len(registry); i++ {
for range len(registry) {
componentStatus := <-ch
if len(componentStatus.Error) != 0 {
isHealthy = false

View File

@ -17,6 +17,7 @@ package jobmonitor
import (
"context"
"fmt"
"slices"
"strings"
"time"
@ -278,12 +279,7 @@ func (w *monitorController) ListQueues(ctx context.Context) ([]*jm.Queue, error)
}
func skippedUnusedJobType(jobType string) bool {
for _, t := range skippedJobTypes {
if jobType == t {
return true
}
}
return false
return slices.Contains(skippedJobTypes, jobType)
}
func (w *monitorController) PauseJobQueues(ctx context.Context, jobType string) error {

View File

@ -131,7 +131,7 @@ func (m *ManifestListCache) push(ctx context.Context, repo, reference string, ma
// if time exceed, then push a updated manifest list which contains existing manifest
var newMan distribution.Manifest
var err error
for n := 0; n < maxManifestListWait; n++ {
for range maxManifestListWait {
log.Debugf("waiting for the manifest ready, repo %v, tag:%v", repo, reference)
time.Sleep(sleepIntervalSec * time.Second)
newMan, err = m.updateManifestList(ctx, repo, man)
@ -177,7 +177,7 @@ type ManifestCache struct {
// CacheContent ...
func (m *ManifestCache) CacheContent(ctx context.Context, remoteRepo string, man distribution.Manifest, art lib.ArtifactInfo, r RemoteInterface, _ string) {
var waitBlobs []distribution.Descriptor
for n := 0; n < maxManifestWait; n++ {
for n := range maxManifestWait {
time.Sleep(sleepIntervalSec * time.Second)
waitBlobs = m.local.CheckDependencies(ctx, art.Repository, man)
if len(waitBlobs) == 0 {

View File

@ -78,13 +78,13 @@ func (suite *RefreshForProjectsTestSuite) TestRefreshForProjects() {
startProjectID := rand.Int63()
var firstPageProjects, secondPageProjects []*models.Project
for i := 0; i < 50; i++ {
for i := range 50 {
firstPageProjects = append(firstPageProjects, &models.Project{
ProjectID: startProjectID + int64(i),
})
}
for i := 0; i < 10; i++ {
for i := range 10 {
secondPageProjects = append(secondPageProjects, &models.Project{
ProjectID: startProjectID + 50 + int64(i),
})

View File

@ -423,10 +423,7 @@ func (t *transfer) copyBlobByChunk(srcRepo, dstRepo, digest string, sizeFromDesc
// update the start and end for upload
*start = *end + 1
// since both ends are closed intervals, it is necessary to subtract one byte
*end = *start + replicationChunkSize - 1
if *end >= endRange {
*end = endRange
}
*end = min(*start+replicationChunkSize-1, endRange)
t.logger.Infof("copying the blob chunk: %d-%d/%d", *start, *end, sizeFromDescriptor)
_, data, err := t.src.PullBlobChunk(srcRepo, digest, sizeFromDescriptor, *start, *end)

View File

@ -16,6 +16,7 @@ package scan
import (
"context"
"slices"
"github.com/goharbor/harbor/src/controller/artifact"
"github.com/goharbor/harbor/src/controller/artifact/processor/image"
@ -125,11 +126,9 @@ func hasCapability(r *models.Registration, a *artifact.Artifact) bool {
// use allowlist here because currently only docker image is supported by the scanner
// https://github.com/goharbor/pluggable-scanner-spec/issues/2
allowlist := []string{image.ArtifactTypeImage}
for _, t := range allowlist {
if a.Type == t {
if slices.Contains(allowlist, a.Type) {
return r.HasCapability(a.ManifestMediaType)
}
}
return false
}

View File

@ -17,6 +17,7 @@ package scanner
import (
"context"
"fmt"
"slices"
"sync"
"time"
@ -383,13 +384,7 @@ var (
)
func isReservedName(name string) bool {
for _, reservedName := range reservedNames {
if name == reservedName {
return true
}
}
return false
return slices.Contains(reservedNames, name)
}
// MetadataResult metadata or error saved in cache

View File

@ -150,7 +150,7 @@ func (l *Auth) attachGroupParallel(ctx context.Context, ldapUsers []model.User,
g := new(errgroup.Group)
g.SetLimit(workerCount)
for i := 0; i < workerCount; i++ {
for i := range workerCount {
curIndex := i
g.Go(func() error {
userGroups := make([]ugModel.UserGroup, 0)

View File

@ -24,6 +24,7 @@ import (
"os"
"path"
"runtime"
"slices"
"testing"
"github.com/docker/distribution/registry/auth/token"
@ -239,12 +240,10 @@ func (f *fakeSecurityContext) IsSolutionUser() bool {
}
func (f *fakeSecurityContext) Can(ctx context.Context, action rbac.Action, resource rbac.Resource) bool {
if actions, ok := f.rcActions[resource]; ok {
for _, a := range actions {
if a == action {
if slices.Contains(actions, action) {
return true
}
}
}
return false
}

View File

@ -16,6 +16,7 @@ package session
import (
"encoding/gob"
"maps"
"github.com/beego/beego/v2/server/web/session"
@ -51,14 +52,10 @@ func (*gobCodec) Decode(data []byte, v any) error {
switch in := v.(type) {
case map[any]any:
for k, v := range vm {
in[k] = v
}
maps.Copy(in, vm)
case *map[any]any:
m := *in
for k, v := range vm {
m[k] = v
}
maps.Copy(m, vm)
default:
return errors.Errorf("object type invalid, %#v", v)
}

View File

@ -18,6 +18,7 @@ import (
"context"
"errors"
"fmt"
"maps"
"math"
"sync"
"time"
@ -116,9 +117,7 @@ func (c *Context) Build(tracker job.Tracker) (job.Context, error) {
// Copy properties
if len(c.properties) > 0 {
for k, v := range c.properties {
jContext.properties[k] = v
}
maps.Copy(jContext.properties, c.properties)
}
// Refresh config properties
@ -128,9 +127,7 @@ func (c *Context) Build(tracker job.Tracker) (job.Context, error) {
}
props := c.cfgMgr.GetAll(c.sysContext)
for k, v := range props {
jContext.properties[k] = v
}
maps.Copy(jContext.properties, props)
// Set loggers for job
c.lock.Lock()
@ -199,10 +196,7 @@ func createLoggers(jobID string) (logger.Interface, error) {
if lc.Name == logger.NameFile || lc.Name == logger.NameDB {
// Need extra param
fSettings := map[string]any{}
for k, v := range lc.Settings {
// Copy settings
fSettings[k] = v
}
maps.Copy(fSettings, lc.Settings)
if lc.Name == logger.NameFile {
// Append file name param
fSettings["filename"] = fmt.Sprintf("%s.log", jobID)

View File

@ -17,6 +17,7 @@ package impl
import (
"context"
"errors"
"maps"
o "github.com/beego/beego/v2/client/orm"
@ -61,9 +62,7 @@ func (dc *DefaultContext) Build(t job.Tracker) (job.Context, error) {
// Copy properties
if len(dc.properties) > 0 {
for k, v := range dc.properties {
jContext.properties[k] = v
}
maps.Copy(jContext.properties, dc.properties)
}
// Set loggers for job

View File

@ -299,10 +299,7 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
blobChunkCount := (total + blobChunkSize - 1) / blobChunkSize
blobChunks := make([][]*blobModels.Blob, blobChunkCount)
for i, start := 0, 0; i < blobChunkCount; i, start = i+1, start+blobChunkSize {
end := start + blobChunkSize
if end > total {
end = total
}
end := min(start+blobChunkSize, total)
blobChunks[i] = gc.deleteSet[start:end]
}

View File

@ -59,7 +59,7 @@ func TestDelKeys(t *testing.T) {
// helper function
// mock the data in the redis
mock := func(count int, prefix string) {
for i := 0; i < count; i++ {
for i := range count {
err = c.Save(context.TODO(), fmt.Sprintf("%s-%d", prefix, i), "", 0)
assert.NoError(t, err)
}

View File

@ -17,6 +17,7 @@ package scandataexport
import (
"encoding/json"
"fmt"
"maps"
"os"
"path/filepath"
"strconv"
@ -173,9 +174,8 @@ func (sde *ScanDataExport) updateExecAttributes(ctx job.Context, params job.Para
}
// copy old extra
attrsToUpdate := exec.ExtraAttrs
for k, v := range attrs {
attrsToUpdate[k] = v
}
maps.Copy(attrsToUpdate, attrs)
return sde.execMgr.UpdateExtraAttrs(ctx.SystemContext(), execID, attrsToUpdate)
}

View File

@ -1,7 +1,6 @@
package logger
import (
"context"
"fmt"
"os"
"path"
@ -97,8 +96,7 @@ func TestGetLoggersMulti(t *testing.T) {
// Test getting sweepers
func TestGetSweeper(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ctx := t.Context()
_, err := GetSweeper(ctx)
if err == nil {
@ -170,8 +168,7 @@ func TestGetGetter(t *testing.T) {
// Test init
func TestLoggerInit(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ctx := t.Context()
oldJobLoggerCfg := config.DefaultConfig.JobLoggerConfigs
oldLoggerCfg := config.DefaultConfig.LoggerConfigs

View File

@ -99,10 +99,8 @@ func tailLogFile(filename string, limit int64) ([]byte, error) {
}
defer fi.Close()
pos := size - sizeToRead
if pos < 0 {
pos = 0
}
pos := max(size-sizeToRead, 0)
if pos != 0 {
_, err = fi.Seek(pos, 0)
if err != nil {

View File

@ -16,6 +16,7 @@ package logger
import (
"reflect"
"slices"
"strings"
"github.com/goharbor/harbor/src/jobservice/logger/backend"
@ -89,13 +90,7 @@ func IsKnownLevel(level string) bool {
return false
}
for _, lvl := range debugLevels {
if lvl == strings.ToUpper(level) {
return true
}
}
return false
return slices.Contains(debugLevels, strings.ToUpper(level))
}
// GetLoggerName return a logger name by Interface

View File

@ -17,6 +17,7 @@ package period
import (
"context"
"fmt"
"maps"
"math/rand"
"time"
@ -303,9 +304,7 @@ func cloneParameters(params job.Parameters, epoch int64) job.Parameters {
p := make(job.Parameters)
// Clone parameters to a new param map
for k, v := range params {
p[k] = v
}
maps.Copy(p, params)
p[PeriodicExecutionMark] = fmt.Sprintf("%d", epoch)

View File

@ -19,7 +19,7 @@ import (
)
func BenchmarkDefaultCodecEncode(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
codec.Encode("abcdefghigklmopqrztuvwxyz")
}
}
@ -27,7 +27,7 @@ func BenchmarkDefaultCodecEncode(b *testing.B) {
func BenchmarkDefaultCodecDecode(b *testing.B) {
data := []byte("abcdefghigklmopqrztuvwxyz")
for i := 0; i < b.N; i++ {
for b.Loop() {
var str string
codec.Decode(data, &str)
}

View File

@ -103,7 +103,7 @@ func (suite *FetchOrSaveTestSuite) TestSaveCalledOnlyOneTime() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
for range 1000 {
wg.Add(1)
go func() {

View File

@ -112,14 +112,14 @@ func (suite *CacheTestSuite) TestPing() {
func (suite *CacheTestSuite) TestScan() {
seed := func(n int) {
for i := 0; i < n; i++ {
for i := range n {
key := fmt.Sprintf("test-scan-%d", i)
err := suite.cache.Save(suite.ctx, key, "")
suite.NoError(err)
}
}
clean := func(n int) {
for i := 0; i < n; i++ {
for i := range n {
key := fmt.Sprintf("test-scan-%d", i)
err := suite.cache.Delete(suite.ctx, key)
suite.NoError(err)

View File

@ -112,14 +112,14 @@ func (suite *CacheTestSuite) TestPing() {
func (suite *CacheTestSuite) TestScan() {
seed := func(n int) {
for i := 0; i < n; i++ {
for i := range n {
key := fmt.Sprintf("test-scan-%d", i)
err := suite.cache.Save(suite.ctx, key, "")
suite.NoError(err)
}
}
clean := func(n int) {
for i := 0; i < n; i++ {
for i := range n {
key := fmt.Sprintf("test-scan-%d", i)
err := suite.cache.Delete(suite.ctx, key)
suite.NoError(err)

View File

@ -26,7 +26,7 @@ func TestKeyMutex(t *testing.T) {
key := "key"
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
for range 100 {
wg.Add(1)
go func() {

View File

@ -220,7 +220,7 @@ func RobotPrefix(ctx context.Context) string {
// SplitAndTrim ...
func SplitAndTrim(s, sep string) []string {
res := make([]string, 0)
for _, s := range strings.Split(s, sep) {
for s := range strings.SplitSeq(s, sep) {
if e := strings.TrimSpace(s); len(e) > 0 {
res = append(res, e)
}
@ -269,8 +269,7 @@ func AuditLogEventEnabled(ctx context.Context, eventType string) bool {
return true
}
disableListStr := DefaultMgr().Get(ctx, common.AuditLogEventsDisabled).GetString()
disableList := strings.Split(disableListStr, ",")
for _, t := range disableList {
for t := range strings.SplitSeq(disableListStr, ",") {
tName := strings.TrimSpace(t)
if strings.EqualFold(tName, eventType) {
return false

View File

@ -27,7 +27,7 @@ func TestAddTask(t *testing.T) {
taskNum := 3
taskInterval := time.Duration(0)
for i := 0; i < taskNum; i++ {
for i := range taskNum {
fn := func(ctx context.Context) {
t.Logf("Task %d is running...", i)
}
@ -64,8 +64,7 @@ func TestStartAndStop(t *testing.T) {
pool.tasks = []*task{t1, t2}
ctx1, cancel1 := context.WithCancel(context.Background())
defer cancel1()
ctx1 := t.Context()
pool.Start(ctx1)
// Let it run for a bit

View File

@ -54,11 +54,11 @@ func (l Links) String() string {
// e.g. <http://example.com/TheBook/chapter2>; rel="previous"; title="previous chapter" , <http://example.com/TheBook/chapter4>; rel="next"; title="next chapter"
func ParseLinks(str string) Links {
var links Links
for _, lk := range strings.Split(str, ",") {
for lk := range strings.SplitSeq(str, ",") {
link := &Link{
Attrs: map[string]string{},
}
for _, attr := range strings.Split(lk, ";") {
for attr := range strings.SplitSeq(lk, ";") {
attr = strings.TrimSpace(attr)
if len(attr) == 0 {
continue

View File

@ -17,6 +17,7 @@ package log
import (
"fmt"
"io"
"maps"
"os"
"runtime"
"sort"
@ -122,12 +123,8 @@ func (l *Logger) WithFields(fields Fields) *Logger {
if len(fields) > 0 {
copyFields := make(map[string]any, len(l.fields)+len(fields))
for key, value := range l.fields {
copyFields[key] = value
}
for key, value := range fields {
copyFields[key] = value
}
maps.Copy(copyFields, l.fields)
maps.Copy(copyFields, fields)
sortedKeys := make([]string, 0, len(copyFields))
for key := range copyFields {

View File

@ -79,7 +79,7 @@ func parseModel(model any) *metadata {
Keys: map[string]*key{},
}
// parse fields of the provided model
for i := 0; i < t.NumField(); i++ {
for i := range t.NumField() {
field := t.Field(i)
orm := field.Tag.Get("orm")
// isn't the database column, skip
@ -107,7 +107,7 @@ func parseModel(model any) *metadata {
}
// parse filter methods of the provided model
for i := 0; i < ptr.NumMethod(); i++ {
for i := range ptr.NumMethod() {
methodName := ptr.Method(i).Name
if !strings.HasPrefix(methodName, "FilterBy") {
continue
@ -173,7 +173,7 @@ func parseFilterable(field reflect.StructField) bool {
// }
func parseSortable(field reflect.StructField) (*q.Sort, bool) {
var defaultSort *q.Sort
for _, item := range strings.Split(field.Tag.Get("sort"), ";") {
for item := range strings.SplitSeq(field.Tag.Get("sort"), ";") {
// isn't sortable, return directly
if item == "false" {
return nil, false
@ -202,7 +202,7 @@ func parseSortable(field reflect.StructField) (*q.Sort, bool) {
// It returns "customized_field1" for "Field1" and returns "field2" for "Field2"
func parseColumn(field reflect.StructField) string {
column := ""
for _, item := range strings.Split(field.Tag.Get("orm"), ";") {
for item := range strings.SplitSeq(field.Tag.Get("orm"), ";") {
if !strings.HasPrefix(item, "column") {
continue
}
@ -224,7 +224,7 @@ func snakeCase(str string) string {
runes := []rune(str)
var out []rune
for i := 0; i < len(runes); i++ {
for i := range len(runes) {
if i > 0 &&
(unicode.IsUpper(runes[i])) &&
((i+1 < len(runes) && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {

View File

@ -271,7 +271,7 @@ func Escape(str string) string {
// e.g. n=3, returns "?,?,?"
func ParamPlaceholderForIn(n int) string {
placeholders := []string{}
for i := 0; i < n; i++ {
for range n {
placeholders = append(placeholders, "?")
}
return strings.Join(placeholders, ",")

View File

@ -387,7 +387,7 @@ func (suite *OrmSuite) TestReadOrCreateParallel() {
arr := make([]int, count)
var wg sync.WaitGroup
for i := 0; i < count; i++ {
for i := range count {
wg.Add(1)
go func(i int) {
defer wg.Done()

View File

@ -58,8 +58,8 @@ func parseKeywords(q string) (map[string]any, error) {
} else {
log.Errorf("failed to unescape the query %s: %v", q, err)
}
params := strings.Split(q, ",")
for _, param := range params {
for param := range strings.SplitSeq(q, ",") {
strs := strings.SplitN(param, "=", 2)
if len(strs) != 2 || len(strs[0]) == 0 || len(strs[1]) == 0 {
return nil, errors.New(nil).
@ -82,7 +82,7 @@ func ParseSorting(sort string) []*Sort {
return []*Sort{}
}
var sorts []*Sort
for _, sorting := range strings.Split(sort, ",") {
for sorting := range strings.SplitSeq(sort, ",") {
key := sorting
desc := false
if strings.HasPrefix(sorting, "-") {
@ -176,8 +176,8 @@ func parseList(value string, c rune) ([]any, error) {
return nil, fmt.Errorf(`and list must start with "(" and end with ")"`)
}
var vs []any
strs := strings.Split(value[1:length-1], " ")
for _, str := range strs {
for str := range strings.SplitSeq(value[1:length-1], " ") {
v := parseValue(str)
if s, ok := v.(string); ok && len(s) == 0 {
continue

View File

@ -14,6 +14,8 @@
package q
import "maps"
// KeyWords ...
type KeyWords = map[string]any
@ -56,9 +58,8 @@ func MustClone(query *Query) *Query {
if query != nil {
q.PageNumber = query.PageNumber
q.PageSize = query.PageSize
for k, v := range query.Keywords {
q.Keywords[k] = v
}
maps.Copy(q.Keywords, query.Keywords)
for _, sort := range query.Sorts {
q.Sorts = append(q.Sorts, &Sort{
Key: sort.Key,

View File

@ -34,7 +34,7 @@ func TestGetRegistryClient(t *testing.T) {
assert.NotNil(t, client)
// multiple calls should return the same client
for i := 0; i < 10; i++ {
for range 10 {
newClient, err := GetRegistryClient()
assert.NoError(t, err)
assert.Equal(t, client, newClient)
@ -55,7 +55,7 @@ func TestGetHarborClient(t *testing.T) {
assert.NotNil(t, client)
// multiple calls should return the same client
for i := 0; i < 10; i++ {
for range 10 {
newClient, err := GetHarborClient()
assert.NoError(t, err)
assert.Equal(t, client, newClient)

View File

@ -15,6 +15,7 @@
package index
import (
"slices"
"sync"
"github.com/goharbor/harbor/src/lib/errors"
@ -80,12 +81,10 @@ func Get(kind, decoration, pattern, extras string) (selector.Selector, error) {
}
item := v.(*indexedItem)
for _, dec := range item.Meta.Decorations {
if dec == decoration {
if slices.Contains(item.Meta.Decorations, decoration) {
factory := item.Factory
return factory(decoration, pattern, extras), nil
}
}
return nil, errors.Errorf("decoration %s of selector %s is not supported", decoration, kind)
}

View File

@ -28,7 +28,7 @@ func (s *testSuite) TestSetAndGet() {
s.Nil(err)
s.Nil(l)
var longList []models.CVEAllowlistItem
for i := 0; i < 50; i++ {
for range 50 {
longList = append(longList, models.CVEAllowlistItem{CVEID: "CVE-1999-0067"})
}

View File

@ -16,6 +16,7 @@ package allowlist
import (
"fmt"
"strings"
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
)
@ -45,6 +46,13 @@ func IsInvalidErr(err error) bool {
func Validate(wl models2.CVEAllowlist) error {
m := map[string]struct{}{}
for _, it := range wl.Items {
cveID := strings.TrimSpace(it.CVEID)
// Check for empty or whitespace-only CVE IDs
if cveID == "" {
return &invalidErr{fmt.Sprintf("empty or whitespace-only CVE ID in allowlist")}
}
// Check for duplicates
if _, ok := m[it.CVEID]; ok {
return &invalidErr{fmt.Sprintf("duplicate CVE ID in allowlist: %s", it.CVEID)}
}

View File

@ -53,6 +53,22 @@ func TestValidate(t *testing.T) {
l models2.CVEAllowlist
noError bool
}{
{
l: models2.CVEAllowlist{
Items: []models2.CVEAllowlistItem{
{CVEID: ""},
},
},
noError: false,
},
{
l: models2.CVEAllowlist{
Items: []models2.CVEAllowlistItem{
{CVEID: " "},
},
},
noError: false,
},
{
l: models2.CVEAllowlist{
Items: nil,

View File

@ -16,11 +16,11 @@ package dao
import (
"context"
"slices"
"strings"
beegorm "github.com/beego/beego/v2/client/orm"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
@ -197,7 +197,7 @@ func permitEventTypes(includeEventTypes []string) []string {
var filterEvents []string
for _, e := range includeEventTypes {
event := strings.ToLower(e)
if utils.StringInSlice(event, model.EventTypes) {
if slices.Contains(model.EventTypes, event) {
filterEvents = append(filterEvents, e)
} else if event == model.OtherEvents { // include all other events
filterEvents = append(filterEvents, model.OtherEventTypes...)

View File

@ -75,7 +75,7 @@ func (ok *ObjectKey) Format(keysAndValues ...any) (string, error) {
}
s := ok.namespace
for i := 0; i < len(keysAndValues); i++ {
for i := range len(keysAndValues) {
// even is key
if i%2 == 0 {
key, match := keysAndValues[i].(string)

View File

@ -16,6 +16,7 @@ package inmemory
import (
"context"
"maps"
"sync"
"github.com/goharbor/harbor/src/common"
@ -41,9 +42,7 @@ func (d *Driver) Load(context.Context) (map[string]any, error) {
d.Lock()
defer d.Unlock()
res := make(map[string]any)
for k, v := range d.cfgMap {
res[k] = v
}
maps.Copy(res, d.cfgMap)
return res, nil
}
@ -51,9 +50,7 @@ func (d *Driver) Load(context.Context) (map[string]any, error) {
func (d *Driver) Save(_ context.Context, cfg map[string]any) error {
d.Lock()
defer d.Unlock()
for k, v := range cfg {
d.cfgMap[k] = v
}
maps.Copy(d.cfgMap, cfg)
return nil
}

View File

@ -18,6 +18,7 @@ import (
"context"
"encoding/json"
"io"
"maps"
"net/http"
"net/http/httptest"
"testing"
@ -66,9 +67,7 @@ func ConfigPutHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
log.Fatal(err)
}
for k, v := range cfgs {
configMapForTest[k] = v
}
maps.Copy(configMapForTest, cfgs)
}
func TestHTTPDriver_Save(t *testing.T) {

View File

@ -88,10 +88,7 @@ func (r *redisClientImpl) StopPendingJobs(ctx context.Context, jobType string) (
// use batch to list the job in queue, because the too many object load from a list might cause the redis crash
for startIndex := int64(0); startIndex < int64(size); startIndex += batchSize {
endIndex := startIndex + batchSize
if endIndex > int64(size) {
endIndex = int64(size)
}
endIndex := min(startIndex+batchSize, int64(size))
jobs, err := redis.Strings(conn.Do("LRANGE", redisKeyJobQueue, startIndex, endIndex))
if err != nil {
return []string{}, err

View File

@ -59,7 +59,7 @@ func (s *RedisClientTestSuite) TestUntrackJobStatusInBatch() {
jobIDs := make([]string, 0)
conn := s.redisClient.redisPool.Get()
defer conn.Close()
for i := 0; i < 100; i++ {
for range 100 {
k := utils.GenerateRandomStringWithLen(10)
jobIDs = append(jobIDs, k)
key := rds.KeyJobStats(fmt.Sprintf("{%s}", s.redisClient.namespace), k)
@ -92,7 +92,7 @@ func (s *RedisClientTestSuite) TestStopPendingJobs() {
}
conn := s.redisClient.redisPool.Get()
defer conn.Close()
for i := 0; i < 100; i++ {
for range 100 {
job := jobInfo{
ID: utils.GenerateRandomStringWithLen(10),
Params: utils.GenerateRandomStringWithLen(10),
@ -107,7 +107,7 @@ func (s *RedisClientTestSuite) TestStopPendingJobs() {
}
}
// job without id
for i := 0; i < 10; i++ {
for range 10 {
job := jobInfo{
Params: utils.GenerateRandomStringWithLen(10),
}

View File

@ -173,7 +173,7 @@ func TestConcurrentPublish(t *testing.T) {
}
// Publish in a short interval.
for i := 0; i < 10; i++ {
for range 10 {
Publish(context.TODO(), "topic1", 100)
}

View File

@ -22,6 +22,7 @@ import (
"net/http"
"net/url"
"regexp"
"slices"
"strings"
"sync"
"sync/atomic"
@ -392,11 +393,8 @@ func userInfoFromClaims(c claimsProvider, setting cfgModels.OIDCSetting) (*UserI
}
res.Groups, res.hasGroupClaim = groupsFromClaims(c, setting.GroupsClaim)
if len(setting.AdminGroup) > 0 {
for _, g := range res.Groups {
if g == setting.AdminGroup {
if slices.Contains(res.Groups, setting.AdminGroup) {
res.AdminGroupMember = true
break
}
}
}
return res, nil

View File

@ -36,13 +36,13 @@ func TestExpiration(t *testing.T) {
func TestGC(t *testing.T) {
manager := createManager(1*time.Second, 10, 1*time.Second).(*mgr)
for i := 0; i < 10; i++ {
for i := range 10 {
rn := fmt.Sprintf("project%d/golang", i)
manager.Generate(rn)
}
time.Sleep(2 * time.Second)
assert.Equal(t, uint64(10), manager.size)
for i := 0; i < 1000; i++ {
for i := range 1000 {
rn := fmt.Sprintf("project%d/redis", i)
manager.Generate(rn)
}

View File

@ -329,7 +329,7 @@ func compileRegexpEveryTime(url string) (string, string, error) {
}
func BenchmarkGetAccountRegion(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
for _, url := range urlForBenchmark {
parseAccountRegion(url)
}
@ -337,7 +337,7 @@ func BenchmarkGetAccountRegion(b *testing.B) {
}
func BenchmarkCompileRegexpEveryTime(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
for _, url := range urlForBenchmark {
compileRegexpEveryTime(url)
}

View File

@ -19,6 +19,7 @@ import (
"fmt"
"io"
"net/http"
"slices"
"github.com/opencontainers/go-digest"
@ -195,12 +196,10 @@ func (a adapter) listGcrTagsByRef(repository, reference string) ([]string, strin
}
// for tag as reference
for d, m := range tgs.Manifest {
for _, t := range m.Tag {
if t == reference {
if slices.Contains(m.Tag, reference) {
return m.Tag, d, nil
}
}
}
return nil, "", nil
}

View File

@ -40,7 +40,7 @@ func IsSpecificPath(path string) ([]string, bool) {
return nil, false
}
components := [][]string{}
for _, component := range strings.Split(path, "/") {
for component := range strings.SplitSeq(path, "/") {
strs, ok := IsSpecificPathComponent(component)
if !ok {
return nil, false
@ -113,8 +113,7 @@ func IsSpecificPathComponent(component string) ([]string, bool) {
suffix = component[j+1:]
}
components := []string{}
strs := strings.Split(component[i+1:j], ",")
for _, str := range strs {
for str := range strings.SplitSeq(component[i+1:j], ",") {
components = append(components, prefix+str+suffix)
}
return components, true

View File

@ -49,11 +49,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
return activeTime(artifacts[i]) > activeTime(artifacts[j])
})
i := e.k
if i > len(artifacts) {
i = len(artifacts)
}
i := min(e.k, len(artifacts))
return artifacts[:i], nil
}

View File

@ -46,11 +46,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
return artifacts[i].PulledTime > artifacts[j].PulledTime
})
i := e.n
if i > len(artifacts) {
i = len(artifacts)
}
i := min(e.n, len(artifacts))
return artifacts[:i], nil
}

View File

@ -48,11 +48,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
return artifacts[i].PushedTime > artifacts[j].PushedTime
})
i := e.k
if i > len(artifacts) {
i = len(artifacts)
}
i := min(e.k, len(artifacts))
return artifacts[:i], nil
}

View File

@ -16,6 +16,7 @@ package scanner
import (
"encoding/json"
"slices"
"time"
"github.com/goharbor/harbor/src/lib"
@ -140,15 +141,9 @@ func (r *Registration) HasCapability(manifestMimeType string) bool {
return false
}
for _, capability := range r.Metadata.Capabilities {
for _, mt := range capability.ConsumesMimeTypes {
if mt == manifestMimeType {
return true
}
}
}
return false
return slices.ContainsFunc(r.Metadata.Capabilities, func(c *v1.ScannerCapability) bool {
return slices.Contains(c.ConsumesMimeTypes, manifestMimeType)
})
}
// GetProducesMimeTypes returns produces mime types for the artifact
@ -162,13 +157,11 @@ func (r *Registration) GetProducesMimeTypes(mimeType string, scanType string) []
capType = v1.ScanTypeVulnerability
}
if scanType == capType {
for _, mt := range capability.ConsumesMimeTypes {
if mt == mimeType {
if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
return capability.ProducesMimeTypes
}
}
}
}
return nil
}
@ -180,12 +173,10 @@ func (r *Registration) GetCapability(mimeType string) *v1.ScannerCapability {
}
for _, capability := range r.Metadata.Capabilities {
for _, mt := range capability.ConsumesMimeTypes {
if mt == mimeType {
if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
return capability
}
}
}
return nil
}

View File

@ -16,6 +16,7 @@ package export
import (
"context"
"slices"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/controller/artifact"
@ -170,12 +171,10 @@ func (dfp *DefaultFilterProcessor) ProcessLabelFilter(_ context.Context, labelID
// TODO (as now there should not have many labels, so here just use
// for^2, we can convert to use map to reduce the time complex if needed. )
for _, label := range art.Labels {
for _, labelID := range labelIDs {
if labelID == label.ID {
if slices.Contains(labelIDs, label.ID) {
return true
}
}
}
return false
}

View File

@ -101,10 +101,7 @@ func GenerateNativeSummary(r *scan.Report, _ ...Option) (any, error) {
sum.ReportID = r.UUID
sum.StartTime = r.StartTime
sum.EndTime = r.EndTime
sum.Duration = r.EndTime.Unix() - r.StartTime.Unix()
if sum.Duration < 0 {
sum.Duration = 0
}
sum.Duration = max(r.EndTime.Unix()-r.StartTime.Unix(), 0)
sum.ScanStatus = job.ErrorStatus.String()
if job.Status(r.Status).Code() != -1 {

View File

@ -17,6 +17,7 @@ package v1
import (
"encoding/json"
"fmt"
"slices"
"github.com/goharbor/harbor/src/lib/errors"
)
@ -95,26 +96,15 @@ func (md *ScannerAdapterMetadata) Validate() error {
for _, ca := range md.Capabilities {
// v1.MimeTypeDockerArtifact is required now
found := false
for _, cm := range ca.ConsumesMimeTypes {
if cm == MimeTypeDockerArtifact {
found = true
break
}
}
found := slices.Contains(ca.ConsumesMimeTypes, MimeTypeDockerArtifact)
if !found {
return errors.Errorf("missing %s in consumes_mime_types", MimeTypeDockerArtifact)
}
// either of v1.MimeTypeNativeReport OR v1.MimeTypeGenericVulnerabilityReport is required
found = false
for _, pm := range ca.ProducesMimeTypes {
if isSupportedMimeType(pm) {
found = true
break
}
}
found = slices.ContainsFunc(ca.ProducesMimeTypes, func(pm string) bool {
return isSupportedMimeType(pm)
})
if !found {
return errors.Errorf("missing %s or %s in produces_mime_types", MimeTypeNativeReport, MimeTypeGenericVulnerabilityReport)
}
@ -124,36 +114,23 @@ func (md *ScannerAdapterMetadata) Validate() error {
}
func isSupportedMimeType(mimeType string) bool {
for _, mt := range supportedMimeTypes {
if mt == mimeType {
return true
}
}
return false
return slices.Contains(supportedMimeTypes, mimeType)
}
// HasCapability returns true when mine type of the artifact support by the scanner
func (md *ScannerAdapterMetadata) HasCapability(mimeType string) bool {
for _, capability := range md.Capabilities {
for _, mt := range capability.ConsumesMimeTypes {
if mt == mimeType {
return true
}
}
}
return false
return slices.ContainsFunc(md.Capabilities, func(c *ScannerCapability) bool {
return slices.Contains(c.ConsumesMimeTypes, mimeType)
})
}
// GetCapability returns capability for the mime type
func (md *ScannerAdapterMetadata) GetCapability(mimeType string) *ScannerCapability {
for _, capability := range md.Capabilities {
for _, mt := range capability.ConsumesMimeTypes {
if mt == mimeType {
if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
return capability
}
}
}
return nil
}

View File

@ -18,6 +18,7 @@ import (
"context"
"fmt"
"io"
"maps"
"sync"
"time"
@ -206,9 +207,7 @@ func (mgr *systemArtifactManager) Cleanup(ctx context.Context) (int64, int64, er
// iterate through this copy to invoke the cleanup
registeredCriteria := make(map[string]Selector, 0)
mgr.lock.Lock()
for key, val := range mgr.cleanupCriteria {
registeredCriteria[key] = val
}
maps.Copy(registeredCriteria, mgr.cleanupCriteria)
mgr.lock.Unlock()
for key, val := range registeredCriteria {

View File

@ -224,7 +224,7 @@ func (e *executionDAO) GetMetrics(ctx context.Context, id int64) (*Metrics, erro
func (e *executionDAO) RefreshStatus(ctx context.Context, id int64) (bool, string, error) {
// as the status of the execution can be refreshed by multiple operators concurrently
// we use the optimistic locking to avoid the conflict and retry 5 times at most
for i := 0; i < 5; i++ {
for range 5 {
statusChanged, currentStatus, retry, err := e.refreshStatus(ctx, id)
if err != nil {
return false, "", err

View File

@ -256,7 +256,7 @@ func (t *taskDAOTestSuite) TestGetMaxEndTime() {
func (t *taskDAOTestSuite) TestUpdateStatusInBatch() {
jobIDs := make([]string, 0)
taskIDs := make([]int64, 0)
for i := 0; i < 300; i++ {
for i := range 300 {
jobID := fmt.Sprintf("job-%d", i)
tid, err := t.taskDAO.Create(t.ctx, &Task{
JobID: jobID,
@ -272,7 +272,7 @@ func (t *taskDAOTestSuite) TestUpdateStatusInBatch() {
err := t.taskDAO.UpdateStatusInBatch(t.ctx, jobIDs, "Stopped", 10)
t.Require().Nil(err)
for i := 0; i < 300; i++ {
for i := range 300 {
tasks, err := t.taskDAO.List(t.ctx, &q.Query{
Keywords: q.KeyWords{"job_id": jobIDs[i]}})
t.Require().Nil(err)

View File

@ -165,11 +165,7 @@ func (sj *SweepJob) sweep(ctx job.Context, vendorType string, retainCount int64)
return errStop
}
// calculate the batch position
j := i + sweepBatchSize
// avoid overflow
if j > total {
j = total
}
j := min(i+sweepBatchSize, total)
if err = sj.mgr.Clean(ctx.SystemContext(), candidates[i:j]); err != nil {
sj.logger.Errorf("[%s] failed to batch clean candidates, error: %v", vendorType, err)

View File

@ -99,4 +99,38 @@ describe('SecurityComponent', () => {
'CVE-2019-789'
);
});
it('should not allow empty and whitespace CVEs', async () => {
// set cveIds with mix empty and whitespace
component.cveIds = `
, , \n , \t, ,
`;
component.addToSystemAllowlist();
const finalIds = component.systemAllowlist.items.map(i => i.cve_id);
expect(finalIds).not.toContain(' ');
expect(finalIds).not.toContain('\n');
expect(finalIds).not.toContain(''); // no empty CVEs
// modal should be closed
expect(component.cveIds).toBeNull();
expect(component.showAddModal).toBeFalse();
});
it('should add only unique CVEs to the allowlist', () => {
// set cveIds with duplicates and valid
component.cveIds = `
CVE-2024-0002,
CVE-2024-0002,
CVE-2024-0004
`;
component.addToSystemAllowlist();
const finalIds = component.systemAllowlist.items.map(i => i.cve_id);
expect(finalIds).toContain('CVE-2024-0004');
expect(finalIds).not.toContain(''); // no empty CVEs
expect(finalIds.filter(id => id === 'CVE-2024-0002').length).toBe(1); // no duplicates
// modal should be closed
expect(component.cveIds).toBeNull();
expect(component.showAddModal).toBeFalse();
});
});

View File

@ -174,7 +174,12 @@ export class SecurityComponent implements OnInit, OnDestroy {
this.systemAllowlist.items.forEach(item => {
map[item.cve_id] = true;
});
this.cveIds.split(/[\n,]+/).forEach(id => {
const newCveIds = this.cveIds
.split(/[\n,]+/)
.map(id => id.trim()) // remove leading/trailing whitespace
.filter(id => id.length > 0); // skip empty or whitespace-only strings
newCveIds.forEach(id => {
let cveObj: any = {};
cveObj.cve_id = id.trim();
if (!map[cveObj.cve_id]) {

View File

@ -169,6 +169,46 @@ describe('ProjectPolicyConfigComponent', () => {
.GenerateSbomOnPush
).toBeTruthy();
});
it('should not allow empty and whitespace CVEs', async () => {
// set cveIds with mix of empty and whitespace
component.projectPolicyConfigComponent.cveIds = `
, , \n , \t, ,
`;
component.projectPolicyConfigComponent.addToProjectAllowlist();
const finalIds =
component.projectPolicyConfigComponent.projectAllowlist.items.map(
i => i.cve_id
);
expect(finalIds).not.toContain(' ');
expect(finalIds).not.toContain('\n');
expect(finalIds).not.toContain(''); // no empty CVEs
// modal should be closed
expect(component.projectPolicyConfigComponent.cveIds).toBeNull();
expect(component.projectPolicyConfigComponent.showAddModal).toBeFalse();
});
it('should add only unique CVEs to the allowlist', () => {
// set cveIds with duplicates and valid
component.projectPolicyConfigComponent.cveIds = `
CVE-2024-0002,
CVE-2024-0002,
CVE-2024-0004
`;
component.projectPolicyConfigComponent.addToProjectAllowlist();
const finalIds =
component.projectPolicyConfigComponent.projectAllowlist.items.map(
i => i.cve_id
);
expect(finalIds).toContain('CVE-2024-0004');
expect(finalIds).not.toContain(''); // no empty CVEs
expect(finalIds.filter(id => id === 'CVE-2024-0002').length).toBe(1); // no duplicates
// modal should be closed
expect(component.projectPolicyConfigComponent.cveIds).toBeNull();
expect(component.projectPolicyConfigComponent.showAddModal).toBeFalse();
});
it('should get hasChangeConfigRole', () => {
expect(
component.projectPolicyConfigComponent.hasChangeConfigRole

View File

@ -13,7 +13,10 @@
// limitations under the License.
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { compareValue, clone } from '../../../../shared/units/utils';
import { ProjectService } from '../../../../shared/services';
import {
ProjectCVEAllowlist,
ProjectService,
} from '../../../../shared/services';
import { ErrorHandler } from '../../../../shared/units/error-handler';
import { State, SystemCVEAllowlist } from '../../../../shared/services';
import {
@ -133,8 +136,8 @@ export class ProjectPolicyConfigComponent implements OnInit {
userProjectAllowlist = false;
systemAllowlistOrProjectAllowlist: string;
systemAllowlistOrProjectAllowlistOrigin: string;
projectAllowlist;
projectAllowlistOrigin;
projectAllowlist: ProjectCVEAllowlist;
projectAllowlistOrigin: ProjectCVEAllowlist;
speedUnit = BandwidthUnit.KB;
speedUnits = [
{
@ -454,7 +457,12 @@ export class ProjectPolicyConfigComponent implements OnInit {
this.projectAllowlist.items.forEach(item => {
map[item.cve_id] = true;
});
this.cveIds.split(/[\n,]+/).forEach(id => {
const newCveIds = this.cveIds
.split(/[\n,]+/)
.map(id => id.trim()) // remove leading/trailing whitespace
.filter(id => id.length > 0); // skip empty or whitespace-only strings
newCveIds.forEach(id => {
let cveObj: any = {};
cveObj.cve_id = id.trim();
if (!map[cveObj.cve_id]) {

View File

@ -433,6 +433,11 @@ export interface ProjectRootInterface {
VALUE: number;
LABEL: string;
}
export interface ProjectCVEAllowlist {
id?: number;
expires_at?: number;
items?: Array<{ cve_id: string }>;
}
export interface SystemCVEAllowlist {
id?: number;
project_id?: number;

View File

@ -329,7 +329,7 @@ func proxyManifestHead(ctx context.Context, w http.ResponseWriter, ctl proxy.Con
// Then GET the image by digest, in order to associate the tag with the digest
// Ensure tag after head request, make sure tags in proxy cache keep update
bCtx := orm.Context()
for i := 0; i < ensureTagMaxRetry; i++ {
for range ensureTagMaxRetry {
time.Sleep(ensureTagInterval)
bArt := lib.ArtifactInfo{ProjectName: art.ProjectName, Repository: art.Repository, Digest: string(desc.Digest)}
err := ctl.EnsureTag(bCtx, bArt, art.Tag)

View File

@ -17,6 +17,7 @@ package vulnerable
import (
"fmt"
"net/http"
"slices"
"github.com/goharbor/harbor/src/controller/artifact/processor/cnab"
"github.com/goharbor/harbor/src/controller/artifact/processor/image"
@ -110,14 +111,12 @@ func Middleware() func(http.Handler) http.Handler {
if art.IsImageIndex() {
// artifact is image index, skip the checking when it is in the allowlist
skippingAllowlist := []string{image.ArtifactTypeImage, cnab.ArtifactTypeCNAB}
for _, t := range skippingAllowlist {
if art.Type == t {
if slices.Contains(skippingAllowlist, art.Type) {
logger.Debugf("artifact %s@%s is image index and its type is %s in skipping allowlist, "+
"skip the vulnerability prevention checking", art.RepositoryName, art.Digest, art.Type)
return nil
}
}
}
if !vulnerable.IsScanSuccess() {
msg := fmt.Sprintf(`current image with "%s" status of vulnerability scanning cannot be pulled due to configured policy in 'Prevent images with vulnerability severity of "%s" or higher from running.' `+

View File

@ -147,12 +147,7 @@ func pickItems(items []string, n *int, last string) ([]string, string) {
i = sort.Search(len(items), func(ix int) bool { return strings.Compare(items[ix], last) > 0 })
}
j := i + *n
if j >= len(items) {
j = len(items)
}
j := min(i+*n, len(items))
result := items[i:j]
nextLast := ""

View File

@ -17,6 +17,7 @@ package handler
import (
"context"
"fmt"
"slices"
"strconv"
"strings"
"sync"
@ -795,13 +796,12 @@ func (a *projectAPI) validateProjectReq(ctx context.Context, req *models.Project
if err != nil {
return fmt.Errorf("failed to get the registry %d: %v", *req.RegistryID, err)
}
permitted := false
for _, t := range config.GetPermittedRegistryTypesForProxyCache() {
if string(registry.Type) == t {
if slices.Contains(config.GetPermittedRegistryTypesForProxyCache(), string(registry.Type)) {
permitted = true
break
}
}
if !permitted {
return errors.BadRequestError(fmt.Errorf("unsupported registry type %s", string(registry.Type)))
}

View File

@ -48,7 +48,7 @@ func parseScanReportMimeTypes(header *string) []string {
var mimeTypes []string
if header != nil {
for _, mimeType := range strings.Split(*header, ",") {
for mimeType := range strings.SplitSeq(*header, ",") {
mimeType = strings.TrimSpace(mimeType)
switch mimeType {
case v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport:

View File

@ -3,6 +3,7 @@ package job
import (
"fmt"
"math/rand"
"slices"
"github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/job/models"
@ -54,10 +55,5 @@ func (mjc *MockJobClient) GetExecutions(uuid string) ([]job.Stats, error) {
}
func (mjc *MockJobClient) validUUID(uuid string) bool {
for _, u := range mjc.JobUUID {
if uuid == u {
return true
}
}
return false
return slices.Contains(mjc.JobUUID, uuid)
}

View File

@ -53,7 +53,7 @@ func OnAnything(obj any, methodName string) *mock.Call {
}
args := []any{}
for i := 0; i < fnType.NumIn(); i++ {
for range fnType.NumIn() {
args = append(args, mock.Anything)
}

View File

@ -3,5 +3,6 @@
HARBOR_SRC_FOLDER=$(realpath ../../)
echo ${HARBOR_SRC_FOLDER}
docker run -it --privileged -v /var/log/harbor:/var/log/harbor -v /etc/hosts:/etc/hosts -v ${HARBOR_SRC_FOLDER}:/drone -v ${HARBOR_SRC_FOLDER}/tests/harbor_ca.crt:/ca/ca.crt -v /dev/shm:/dev/shm -w /drone registry.goharbor.io/harbor-ci/goharbor/harbor-e2e-engine:latest-ui /bin/bash
# If the testbed network type is private need to set NETWORK_TYPE private, default is public
docker run -it --privileged -v /var/log/harbor:/var/log/harbor -v /etc/hosts:/etc/hosts -v ${HARBOR_SRC_FOLDER}:/drone -v ${HARBOR_SRC_FOLDER}/tests/harbor_ca.crt:/ca/ca.crt -v /dev/shm:/dev/shm -e NETWORK_TYPE=public -w /drone registry.goharbor.io/harbor-ci/goharbor/harbor-e2e-engine:latest-ui /bin/bash

View File

@ -17,6 +17,12 @@ Documentation This resource provides helper functions for docker operations
Library OperatingSystem
Library Process
*** Variables ***
# Define variables for start dockerd within private network
${DOCKERD_CMD} dockerd
${DOCKERD_ARGS} --iptables=false
${LOG_FILE} ./docker-daemon.log
*** Keywords ***
Run Docker Info
[Arguments] ${docker-params}
@ -115,12 +121,22 @@ Get Container IP
# docker:1.13-dind
# If you are running this keyword in a container, make sure it is run with --privileged turned on
Start Docker Daemon Locally
${test_network_type}= Get Environment Variable NETWORK_TYPE public
Log To Console current test_network_type: ${test_network_type}
${pid}= Run pidof dockerd
#${rc} ${output}= Run And Return Rc And Output ./tests/robot-cases/Group0-Util/docker_config.sh
#Log ${output}
#Should Be Equal As Integers ${rc} 0
Return From Keyword If '${pid}' != '${EMPTY}'
OperatingSystem.File Should Exist /usr/local/bin/dockerd-entrypoint.sh
${handle}= Set Variable ""
IF '${test_network_type}' == 'private'
Log To Console network type is private
${handle}= Start Process ${DOCKERD_CMD} ${DOCKERD_ARGS} stdout=${LOG_FILE} stderr=${LOG_FILE} shell=Tr
ELSE IF '${test_network_type}' == 'public'
Log To Console network type is public
${handle}= Start Process /usr/local/bin/dockerd-entrypoint.sh dockerd>./daemon-docker-local.log 2>&1 shell=True
END
${handle}= Start Process /usr/local/bin/dockerd-entrypoint.sh dockerd>./daemon-local.log 2>&1 shell=True
Process Should Be Running ${handle}
FOR ${IDX} IN RANGE 5

View File

@ -4,16 +4,16 @@ In Stratoscale, we really like the idea of API-first services, and we also reall
We saw the go-swagger library, and thought that most of it can really help us. Generating code from
swagger files is a big problem with a lot of corner cases, and go-swagger is doing great job.
The one thing that we felt missing, is customization of the server to run with our design principles:
The one thing that we felt missing was customization of the server to run with our design principles:
* Custom `main` function
* Dependency injection
* Limited scopes with unit testing.
* Limited scope with unit testing.
Also:
* Adding you functions to the generated `configure_swagger_*.go` seems to be a burden.
* Lack of Interface that the service implement.
* Adding your functions to the generated `configure_swagger_*.go` seems to be a burden.
* Lack of Interface that the service implements.
* Complicated and custom http clients and runtime.
Those are the changes that this contributor templates are providing:
@ -22,7 +22,7 @@ Those are the changes that this contributor templates are providing:
### The new `restapi` package exposes interfaces
* Those interfaces can implemented by the developer and are the business logic of the service.
* Those interfaces can be implemented by the developer and are the business logic of the service.
* The implementation of those is extensible.
* The implementation is separated from the generated code.
@ -32,8 +32,8 @@ The `restapi.Handler` (see [example](./example/restapi/configure_swagger_petstor
a standard `http.Handler`
* Given objects that implements the business logic, we can create a simple http handler.
* This handler is standard go http.Handler, so we can now use any other middleware, library, or framework
that support it.
* This handler is a standard go http.Handler, so we can now use any other middleware, library, or framework
that supports it.
* This handler is standard, so we understand it better.
## Client
@ -57,7 +57,7 @@ In the [example package](https://github.com/Stratoscale/swagger/tree/master/exam
### [restapi](https://github.com/Stratoscale/swagger/tree/master/example/restapi)
This package is autogenerated and contains the server routing and parameters parsing.
This package is autogenerated and contains the server routing and parameter parsing.
The modified version contains `restapi.PetAPI` and `restapi.StoreAPI` which were auto generated.
@ -116,7 +116,7 @@ Let's look how we use this generated code to build our server.
### [internal](https://github.com/Stratoscale/swagger/tree/master/example/internal)
The `internal` package is **not** auto generated and contains the business logic of our server.
We can see two structs that implements the `restapi.PetAPI` and `restapi.StoreAPI` interfaces,
We can see two structs that implement the `restapi.PetAPI` and `restapi.StoreAPI` interfaces,
needed to make our server work.
When adding or removing functions from our REST API, we can just add or remove functions to those
@ -189,7 +189,7 @@ type SwaggerPetstore {
}
```
Those fields are objects, which implements interfaces declared in the [pet](./example/client/pet) and
Those fields are objects, which implement interfaces declared in the [pet](./example/client/pet) and
[store](./example/client/store) packages:
For example:
@ -234,7 +234,7 @@ security:
The securityDefinitions section defines different security types that your application can handle.
The supported types by go-swagger are:
* `apiKey` - token that should be able to processed.
* `apiKey` - token that should be able to be processed.
* `oauth2` - token and scopes that should be processed.
* and `basic` - user/password that should be processed.
@ -253,7 +253,7 @@ paths:
- token: [admin]
```
Here we overridden the scope of token in the POST /pets URL so that only admin can use this API.
Here we have overridden the scope of the token in the POST /pets URL so that only admin can use this API.
Let's see how we can use this functionality.
@ -289,14 +289,14 @@ type Config struct {
This one is a custom defined function that gets the request and can return an error.
If the returned error is not nil, and 403 HTTP error will be returned to the client - here the policy
enforcement comes to place.
enforcement comes into play.
There are two things that this function should be aware of:
1. The user - it can retrieve the user information from the context: `ctx.Value(restapi.AuthKey).(MyUserType)`.
Usually, a server will have a function for extracting this user information and returns a concrete
type which could be used by all the routes.
2. The route - it can retrieve the route using the go-swagger function: `middleware.MatchedRouteFrom(*http.Request)`.
So no need to parse URL and test the request method.
So no need to parse the URL and test the request method.
This route struct contains the route information. If for example, we want to check the scopes that were
defined for the current route in the swagger.yaml we can use the code below: