{"record":{"id":"05e87a9b07016bd4","repo":"juanfont/headscale","slug":"q-w","errorCode":null,"errorMessage":"%q %w","messagePattern":"%q %w","errorType":"validation","errorClass":"ErrSSHActionInvalid","httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":1642,"sourceCode":"// String returns the string representation of the [SSHAction].\nfunc (a *SSHAction) String() string {\n\treturn string(*a)\n}\n\n// UnmarshalJSON trims surrounding whitespace before matching, lets the\n// empty string through (per-rule Validate() surfaces it later), and\n// rejects every other unknown value here.\nfunc (a *SSHAction) UnmarshalJSON(b []byte) error {\n\tstr := strings.TrimSpace(strings.Trim(string(b), `\"`))\n\tswitch str {\n\tcase \"\":\n\t\t*a = SSHAction(\"\")\n\tcase \"accept\":\n\t\t*a = SSHActionAccept\n\tcase \"check\":\n\t\t*a = SSHActionCheck\n\tdefault:\n\t\treturn fmt.Errorf(\"%q %w\", str, ErrSSHActionInvalid)\n\t}\n\n\treturn nil\n}\n\n// MarshalJSON implements JSON marshaling for [SSHAction].\nfunc (a *SSHAction) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(string(*a))\n}\n\n// Protocol represents a network protocol with its IANA number and descriptions.\ntype Protocol string\n\nconst (\n\tProtocolNameICMP     Protocol = \"icmp\"\n\tProtocolNameIGMP     Protocol = \"igmp\"\n\tProtocolNameIPv4     Protocol = \"ipv4\"\n\tProtocolNameIPInIP   Protocol = \"ip-in-ip\"","sourceCodeStart":1624,"sourceCodeEnd":1660,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L1624-L1660","documentation":"SSHAction.UnmarshalJSON accepts \"accept\", \"check\", or an empty string (surfaced later by per-rule Validate). Anything else returns ErrSSHActionInvalid with the value.","triggerScenarios":"An ssh rule with \"action\": \"deny\" or a typo like \"accep\" — SSH rules cannot deny, they only grant accept or check-session modes.","commonSituations":"Writing deny-style SSH rules; copy-paste from ACL actions assuming the same vocabulary; trailing whitespace variants other than simple trim cases.","solutions":["Use \"accept\" or \"check\" as the ssh action","Delete rules intended as denies — absence of a rule already denies SSH","Check spelling and quoting of the action string"],"exampleFix":"// before\n{\"action\": \"deny\", \"src\": [...], \"dst\": [...], \"users\": [...]}\n// after\n{\"action\": \"check\", \"src\": [...], \"dst\": [...], \"users\": [...]}","handlingStrategy":"validation","validationCode":"func validSSHAction(s string) bool {\n\tswitch s { case \"\", \"accept\", \"check\": return true }\n\treturn false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["SSH rules only grant access (accept/check); omit rules to deny","Use \"check\" for interactive approval flows instead of deny"],"tags":["policy","ssh","action","validation"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}