package external import ( "bytes" "fmt" "net/http" "os/exec" "strings" ) // selfVisibility reports whether the repository we are adopting into can be read // anonymously. // // This is the half of the confidentiality rule the tool was previously blind to. // Knowing only that a source is private makes the warning fire on every adoption // a private repository performs, which is the legitimate case — and a warning // that always fires is a warning nobody reads. // // The signal is coarse on purpose. It distinguishes public from not-public and // nothing finer, so it cannot see that two repositories are private to different // groups. That case widens access and this check will miss it. func selfVisibility(root string) (public bool, known bool) { out, err := exec.Command("git", "-C", root, "remote", "get-url", "origin").Output() if err != nil { return false, false } host, ownerRepo, ok := splitRemote(strings.TrimSpace(string(out))) if !ok { return false, false } req, err := http.NewRequest(http.MethodHead, "https://"+host+"/"+ownerRepo, nil) if err != nil { return false, false } resp, err := client.Do(req) if err != nil { return false, false } defer resp.Body.Close() return resp.StatusCode == http.StatusOK, true } // splitRemote pulls a host and owner/repo out of a git remote, whether it is // ssh, ssh:// or https. func splitRemote(remote string) (host, ownerRepo string, ok bool) { s := remote if i := strings.Index(s, "://"); i >= 0 { s = s[i+3:] } if at := strings.Index(s, "@"); at >= 0 { s = s[at+1:] } // scp-style "host:owner/repo.git" and url-style "host:port/owner/repo.git" var rest string if i := strings.IndexAny(s, ":/"); i >= 0 { host, rest = s[:i], s[i+1:] } else { return "", "", false } if j := strings.Index(rest, "/"); j >= 0 && isPort(rest[:j]) { rest = rest[j+1:] } rest = strings.TrimSuffix(strings.Trim(rest, "/"), ".git") if host == "" || strings.Count(rest, "/") != 1 { return "", "", false } return host, rest, true } func isPort(s string) bool { if s == "" { return false } for _, r := range s { if r < '0' || r > '9' { return false } } return true } // notePublishedSurface says when an adopted document did not come from the // publisher's published surface. // // What is not exported is not hidden — the rest of a repository is there to read. // It is simply not what you depend on, and a lock against it records a dependency // on something that was never a contract. func notePublishedSurface(rawURL string, out interface{ Write([]byte) (int, error) }) { if strings.Contains(rawURL, "/"+PublishedDir+"/") { return } var b bytes.Buffer fmt.Fprintf(&b, " NOTE not from %s — what is not exported is not hidden, but it is\n", PublishedDir) fmt.Fprintf(&b, " not what you depend on. Nothing promises this path will still\n") fmt.Fprintf(&b, " be there, or still mean this, tomorrow.\n") out.Write(b.Bytes()) }