Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ cd autocar
go build -trimpath -o autocar ./cmd/autocar
```

源码构建的各选项型命令只接受命名参数,不接受额外的位置参数(包括 `--` 后的内容)。
`token`、`cert`、`init` 遇到这类输入会先报错,不生成凭据或配置文件;
正常参数、`-h/--help` 与既有配置文件覆盖规则保持不变。

### 新部署:生成两端配置并先检查

以下 `init` 与 `--check` 需要包含这些功能的源码构建,已发布的 v1.0.1 不支持。
Expand Down
4 changes: 2 additions & 2 deletions cmd/autocar/bench.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func runBenchServer(ctx context.Context, args []string) error {
allowPublic := fs.Bool("allow-public-benchmark", false, "allow an unauthenticated benchmark listener outside loopback")
maxBytes := fs.Int64("max-bytes", 64<<20, "maximum bytes per transfer")
maxConnections := fs.Int("max-connections", 16, "maximum concurrent transfers")
if err := fs.Parse(args); err != nil {
if err := parseCommandFlags(fs, args); err != nil {
return err
}
if *maxBytes <= 0 || *maxConnections <= 0 {
Expand Down Expand Up @@ -100,7 +100,7 @@ func runBenchClient(parent context.Context, args []string) error {
warmup := fs.Int("warmup", 1, "unmeasured warmup iterations")
timeout := fs.Duration("timeout", 2*time.Minute, "timeout per transfer")
jsonOutput := fs.Bool("json", false, "emit machine-readable JSON")
if err := fs.Parse(args); err != nil {
if err := parseCommandFlags(fs, args); err != nil {
return err
}
if *target == "" {
Expand Down
2 changes: 1 addition & 1 deletion cmd/autocar/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func runCert(args []string) error {
keyFile := fs.String("key", "server.key", "0600 private-key output path")
days := fs.Int("days", 365, "certificate validity in days (1-1825)")
organization := fs.String("organization", "AutoCAR", "certificate organization")
if err := fs.Parse(args); err != nil {
if err := parseCommandFlags(fs, args); err != nil {
return err
}
if strings.TrimSpace(*hosts) == "" {
Expand Down
19 changes: 19 additions & 0 deletions cmd/autocar/cli_arguments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"flag"
"fmt"
)

// None of the option-based commands accepts positional operands. Check them
// before reading credentials, creating output files, or starting network I/O.
// Do not echo the operands: an accidentally pasted value may be a secret.
func parseCommandFlags(fs *flag.FlagSet, args []string) error {
if err := fs.Parse(args); err != nil {
return err
}
if fs.NArg() != 0 {
return fmt.Errorf("%s does not accept positional arguments; use named options", fs.Name())
}
return nil
}
65 changes: 65 additions & 0 deletions cmd/autocar/cli_arguments_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"context"
"os"
"path/filepath"
"strings"
"testing"
)

func TestCredentialCommandsRejectPositionalArgumentsBeforeWriting(t *testing.T) {
const unexpected = "private-unexpected-argument"
for _, command := range []string{"token", "cert", "init"} {
for _, suffix := range [][]string{
{unexpected}, {"--", unexpected}, {unexpected, "--unknown-option"},
} {
t.Run(command+"/"+strings.Join(suffix, " "), func(t *testing.T) {
directory := t.TempDir()
var args, outputs []string
switch command {
case "token":
outputs = []string{filepath.Join(directory, "token")}
args = []string{"token", "--out", outputs[0]}
case "cert":
outputs = []string{filepath.Join(directory, "cert.pem"), filepath.Join(directory, "key.pem")}
args = []string{"cert", "--hosts", "relay.invalid", "--cert", outputs[0], "--key", outputs[1]}
case "init":
outputs = []string{filepath.Join(directory, "bundle")}
args = []string{"init", "--server", "relay.invalid:443", "--out", outputs[0]}
}
err := run(context.Background(), append(args, suffix...))
if err == nil || !strings.Contains(err.Error(), "positional arguments") {
t.Errorf("want positional argument rejection, got %v", err)
}
if err != nil && strings.Contains(err.Error(), unexpected) {
t.Error("error disclosed the unexpected argument value")
}
for _, path := range outputs {
if _, err := os.Lstat(path); !os.IsNotExist(err) {
t.Errorf("output %s was created or could not be checked: %v", filepath.Base(path), err)
}
}
})
}
}
}

func TestBenchmarkCommandsRejectPositionalArgumentsBeforeStartup(t *testing.T) {
// Invalid required settings make the unfixed baseline return before any
// listener or network connection. Argument rejection must precede those
// semantic checks, not be inferred from a downstream startup failure.
for _, args := range [][]string{
{"bench-server", "--listen", "invalid-listener", "unexpected"},
{"bench-server", "--listen", "invalid-listener", "--", "unexpected"},
{"bench-client", "--target=", "unexpected"},
{"bench-client", "--target=", "--", "unexpected"},
} {
t.Run(strings.Join(args, " "), func(t *testing.T) {
err := run(context.Background(), args)
if err == nil || !strings.Contains(err.Error(), "positional arguments") {
t.Fatalf("want argument rejection before startup validation, got %v", err)
}
})
}
}
5 changes: 1 addition & 4 deletions cmd/autocar/config_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@ const maxCommandConfigSize = 64 << 10
// performs its normal semantic and credential validation after parsing.
func parseFlagsWithConfig(fs *flag.FlagSet, args []string) error {
configPath := fs.String("config", "", "JSON options file; CLI overrides it, file paths are relative to its directory")
if err := fs.Parse(args); err != nil {
if err := parseCommandFlags(fs, args); err != nil {
return err
}
if fs.NArg() != 0 {
return errors.New("positional arguments are not supported; use named options")
}
if *configPath == "" {
return nil
}
Expand Down
5 changes: 1 addition & 4 deletions cmd/autocar/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,9 @@ func runInitWith(args []string, output io.Writer) error {
serverName := fs.String("server-name", "", "certificate DNS name or IP; defaults to the relay host")
directory := fs.String("out", "autocar-config", "new private output directory; must not already exist")
days := fs.Int("days", 365, "self-signed certificate validity in days (1-1825)")
if err := fs.Parse(args); err != nil {
if err := parseCommandFlags(fs, args); err != nil {
return err
}
if fs.NArg() != 0 {
return errors.New("init does not accept positional arguments")
}
if strings.TrimSpace(*directory) == "" {
return errors.New("--out must name a new directory")
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/autocar/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func runToken(args []string) error {
fs := flag.NewFlagSet("token", flag.ContinueOnError)
output := fs.String("out", "", "write to a new 0600 file instead of stdout")
bytesCount := fs.Int("bytes", 32, "random byte count (16-128)")
if err := fs.Parse(args); err != nil {
if err := parseCommandFlags(fs, args); err != nil {
return err
}
if *bytesCount < 16 || *bytesCount > 128 {
Expand Down
10 changes: 8 additions & 2 deletions docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ relay and an `auto` client; installing v1.0.1 does not enable web-cover.

## 1. Build and create credentials

Current source builds reject unexpected positional operands in option-based
commands, including operands after `--`; use named options for every setting.
Credential commands (`token`, `cert`, `init`) reject these inputs before creating
files, and benchmark commands reject them before startup. Normal flag syntax and
`-h`/`--help` remain supported. This does not add `--config` to these commands.

### Generate a new paired bundle

With a build containing `init` and `--check` (not the published v1.0.1 binary),
Expand Down Expand Up @@ -429,8 +435,8 @@ separately from QUIC circuit health.
### Reusable configuration files (after v1.0.1)

Builds containing the configuration-file change support `--config` for `client`,
`server`, and `doctor`; the published v1.0.1 binary does not. Existing CLI-only
commands are unchanged. Copy [examples/client.json](../examples/client.json)
`server`, and `doctor`; the published v1.0.1 binary does not. CLI-only commands do
not gain `--config` support. Copy [examples/client.json](../examples/client.json)
and [examples/server.json](../examples/server.json) into your credential
directory and adjust addresses and filenames. These files contain references,
not secrets; keep the existing token and private-key permission requirements.
Expand Down
Loading