@@ -2,14 +2,17 @@ package site
22
33import (
44 "net"
5+ "os"
56 "path/filepath"
67 "regexp"
78 "strconv"
89 "strings"
910 "sync"
1011
1112 "github.com/0xJacky/Nginx-UI/internal/cache"
13+ "github.com/0xJacky/Nginx-UI/internal/nginx"
1214 "github.com/0xJacky/Nginx-UI/internal/upstream"
15+ "github.com/uozi-tech/cosy/logger"
1316)
1417
1518type Index struct {
@@ -50,8 +53,26 @@ func scanForSite(configPath string, content []byte) error {
5053 // Regular expressions for server_name and listen directives
5154 serverNameRegex := regexp .MustCompile (`(?m)^[ \t]*server_name\s+([^;#]+);` )
5255 listenRegex := regexp .MustCompile (`(?m)^[ \t]*listen\s+([^;#]+);` )
56+ includeRegex := regexp .MustCompile (`(?m)^[ \t]*include\s+([^;#]+);` )
5357 returnRegex := regexp .MustCompile (`(?m)^[ \t]*return\s+30[1-8]\s+https://[^\s;#]+` )
5458
59+ baseDir := filepath .Dir (configPath )
60+ baseAbs , err := filepath .Abs (baseDir )
61+ if err != nil {
62+ logger .Debugf ("Failed to resolve base path for config %s: %v" , configPath , err )
63+ return err
64+ }
65+
66+ allowedRoot := baseAbs
67+ if confRoot := nginx .GetConfPath (); confRoot != "" {
68+ confRootAbs , err := filepath .Abs (confRoot )
69+ if err != nil {
70+ logger .Debugf ("Failed to resolve nginx conf root %s: %v" , confRoot , err )
71+ } else {
72+ allowedRoot = confRootAbs
73+ }
74+ }
75+
5576 // Find server blocks
5677 serverBlockRegex := regexp .MustCompile (`(?ms)server\s*\{[^\{]*((.*?\{.*?\})*?[^\}]*)\}` )
5778 serverBlocks := serverBlockRegex .FindAllSubmatch (content , - 1 )
@@ -111,9 +132,68 @@ func scanForSite(configPath string, content []byte) error {
111132 // Check if this server block has HTTPS redirect
112133 hasRedirect := returnRegex .Match (serverBlockContent )
113134
114- // Check if SSL is enabled and extract port
135+ // Collect listen directives from this server block and its includes
115136 listenMatches := listenRegex .FindAllSubmatch (serverBlockContent , - 1 )
116137
138+ // Parse includes inside the server block (common pattern: listen is placed in snippet)
139+ includeMatches := includeRegex .FindAllSubmatch (serverBlockContent , - 1 )
140+ for _ , match := range includeMatches {
141+ if len (match ) < 2 {
142+ continue
143+ }
144+ includePath := strings .TrimSpace (string (match [1 ]))
145+ includePath = strings .Trim (includePath , `"'` )
146+ // Skip wildcard includes for now; they can represent multiple files
147+ if strings .Contains (includePath , "*" ) {
148+ logger .Debugf ("Skipping wildcard include in site scan: %s" , includePath )
149+ continue
150+ }
151+
152+ // Resolve relative paths: prefer same directory; fallback to nginx root (allowedRoot)
153+ if ! filepath .IsAbs (includePath ) {
154+ candidate := filepath .Join (baseDir , includePath )
155+ if _ , err := os .Stat (candidate ); err != nil {
156+ altCandidate := filepath .Join (allowedRoot , includePath )
157+ if _ , altErr := os .Stat (altCandidate ); altErr == nil {
158+ candidate = altCandidate
159+ }
160+ }
161+ includePath = candidate
162+ }
163+
164+ includePath = filepath .Clean (includePath )
165+ includeAbs , err := filepath .Abs (includePath )
166+ if err != nil {
167+ logger .Debugf ("Failed to resolve absolute path for include during site scan: %s, error: %v" , includePath , err )
168+ continue
169+ }
170+
171+ rel , err := filepath .Rel (allowedRoot , includeAbs )
172+ if err != nil || strings .HasPrefix (rel , ".." + string (filepath .Separator )) || rel == ".." {
173+ logger .Debugf ("Blocked include outside base config directory during site scan: %s" , includeAbs )
174+ continue
175+ }
176+
177+ includeContent , err := os .ReadFile (includeAbs )
178+ if err != nil {
179+ logger .Debugf ("Failed to read include file during site scan: %s, error: %v" , includeAbs , err )
180+ continue
181+ }
182+
183+ includeListens := listenRegex .FindAllSubmatch (includeContent , - 1 )
184+ if len (includeListens ) == 0 {
185+ logger .Debugf ("No listen directives found in include file during site scan: %s" , includePath )
186+ continue
187+ }
188+ listenMatches = append (listenMatches , includeListens ... )
189+ }
190+
191+ // If no listen directives were found, assume default HTTP :80 to keep site visible
192+ if len (listenMatches ) == 0 {
193+ logger .Debugf ("No listen directive found for server block in %s, defaulting to :80 http" , configPath )
194+ listenMatches = append (listenMatches , [][]byte {{}, []byte ("80" )})
195+ }
196+
117197 for _ , match := range listenMatches {
118198 if len (match ) >= 2 {
119199 listenValue := strings .TrimSpace (string (match [1 ]))
0 commit comments