<commit_before>package clientcmd

import (
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"

	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/util/sets"

	"github.com/openshift/origin/pkg/api/v1"
	"github.com/openshift/origin/pkg/client"
)

// TestRunGenerators makes sure we catch new generators added to `oc run`
func TestRunGenerators(t *testing.T) {
	f := NewFactory(nil)

	// Contains the run generators we expect to see
	expectedRunGenerators := sets.NewString(
		// kube generators
		"run/v1",
		"run-pod/v1",
		"deployment/v1beta1",
		"job/v1",
		"job/v1beta1",

		// origin generators
		"run-controller/v1", // legacy alias for run/v1
		"deploymentconfig/v1",
	).List()

	runGenerators := sets.StringKeySet(f.Generators("run")).List()
	if !reflect.DeepEqual(expectedRunGenerators, runGenerators) {
		t.Errorf("Expected run generators:%#v, got:\n%#v", expectedRunGenerators, runGenerators)
	}
}

func TestClientConfigForVersion(t *testing.T) {
	called := 0
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if req.URL.Path != "/oapi" {
			t.Errorf("Unexpected path called during negotiation: %s", req.URL.Path)
			return
		}
		called++
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"versions":["v1"]}`))
	}))
	defer server.Close()

	defaultConfig := &restclient.Config{Host: server.URL}
	client.SetOpenShiftDefaults(defaultConfig)

	clients := &clientCache{
		clients:       make(map[string]*client.Client),
		configs:       make(map[string]*restclient.Config),
		defaultConfig: defaultConfig,
	}

	// First call, negotiate
	called = 0
	v1Config, err := clients.ClientConfigForVersion(nil)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if v1Config.GroupVersion.String() != "v1" {
		t.Fatalf("Expected v1, got %v", v1Config.GroupVersion.String())
	}
	if called != 1 {
		t.Fatalf("Expected to be called 1 time during negotiation, was called %d times", called)
	}

	// Second call, cache
	called = 0
	v1Config, err = clients.ClientConfigForVersion(nil)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if v1Config.GroupVersion.String() != "v1" {
		t.Fatalf("Expected v1, got %v", v1Config.GroupVersion.String())
	}
	if called != 0 {
		t.Fatalf("Expected not be called again getting a config from cache, was called %d additional times", called)
	}

	// Third call, cached under exactly matching version
	called = 0
	v1Config, err = clients.ClientConfigForVersion(&v1.SchemeGroupVersion)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if v1Config.GroupVersion.String() != "v1" {
		t.Fatalf("Expected v1, got %v", v1Config.GroupVersion.String())
	}
	if called != 0 {
		t.Fatalf("Expected not be called again getting a config from cache, was called %d additional times", called)
	}

	// Call for removed version, return error
	called = 0
	if _, err := clients.ClientConfigForVersion(&unversioned.GroupVersion{Version: "v1beta3"}); err == nil {
		t.Fatalf("Unexpected non-error: %v", err)
	}
	if called != 1 {
		t.Fatalf("Expected to be called once getting a config for a new version, was called %d times", called)
	}
}

func TestComputeDiscoverCacheDir(t *testing.T) {
	testCases := []struct {
		name      string
		parentDir string
		host      string

		expected string
	}{
		{
			name:      "simple append",
			parentDir: "~/",
			host:      "localhost:8443",
			expected:  "~/localhost_8443",
		},
		{
			name:      "with path",
			parentDir: "~/",
			host:      "localhost:8443/prefix",
			expected:  "~/localhost_8443/prefix",
		},
		{
			name:      "dotted name",
			parentDir: "~/",
			host:      "mine.example.org:8443",
			expected:  "~/mine.example.org_8443",
		},
		{
			name:      "IP",
			parentDir: "~/",
			host:      "127.0.0.1:8443",
			expected:  "~/127.0.0.1_8443",
		},
		{
			// restricted characters from: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#naming_conventions
			// it's not a complete list because they have a very helpful: "Any other character that the target file system does not allow."
			name:      "windows safe",
			parentDir: "~/",
			host:      `<>:"\|?*`,
			expected:  "~/________",
		},
	}

	for _, tc := range testCases {
		actual := computeDiscoverCacheDir(tc.parentDir, tc.host)
		if actual != tc.expected {
			t.Errorf("%s: expected %v, got %v", tc.name, tc.expected, actual)
		}
	}
}
<commit_msg>Generators added scheduled jobs<commit_after>package clientcmd

import (
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"

	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/util/sets"

	"github.com/openshift/origin/pkg/api/v1"
	"github.com/openshift/origin/pkg/client"
)

// TestRunGenerators makes sure we catch new generators added to `oc run`
func TestRunGenerators(t *testing.T) {
	f := NewFactory(nil)

	// Contains the run generators we expect to see
	expectedRunGenerators := sets.NewString(
		// kube generators
		"run/v1",
		"run-pod/v1",
		"deployment/v1beta1",
		"job/v1",
		"job/v1beta1",
		"scheduledjob/v2alpha1",

		// origin generators
		"run-controller/v1", // legacy alias for run/v1
		"deploymentconfig/v1",
	).List()

	runGenerators := sets.StringKeySet(f.Generators("run")).List()
	if !reflect.DeepEqual(expectedRunGenerators, runGenerators) {
		t.Errorf("Expected run generators:%#v, got:\n%#v", expectedRunGenerators, runGenerators)
	}
}

func TestClientConfigForVersion(t *testing.T) {
	called := 0
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		if req.URL.Path != "/oapi" {
			t.Errorf("Unexpected path called during negotiation: %s", req.URL.Path)
			return
		}
		called++
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"versions":["v1"]}`))
	}))
	defer server.Close()

	defaultConfig := &restclient.Config{Host: server.URL}
	client.SetOpenShiftDefaults(defaultConfig)

	clients := &clientCache{
		clients:       make(map[string]*client.Client),
		configs:       make(map[string]*restclient.Config),
		defaultConfig: defaultConfig,
	}

	// First call, negotiate
	called = 0
	v1Config, err := clients.ClientConfigForVersion(nil)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if v1Config.GroupVersion.String() != "v1" {
		t.Fatalf("Expected v1, got %v", v1Config.GroupVersion.String())
	}
	if called != 1 {
		t.Fatalf("Expected to be called 1 time during negotiation, was called %d times", called)
	}

	// Second call, cache
	called = 0
	v1Config, err = clients.ClientConfigForVersion(nil)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if v1Config.GroupVersion.String() != "v1" {
		t.Fatalf("Expected v1, got %v", v1Config.GroupVersion.String())
	}
	if called != 0 {
		t.Fatalf("Expected not be called again getting a config from cache, was called %d additional times", called)
	}

	// Third call, cached under exactly matching version
	called = 0
	v1Config, err = clients.ClientConfigForVersion(&v1.SchemeGroupVersion)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if v1Config.GroupVersion.String() != "v1" {
		t.Fatalf("Expected v1, got %v", v1Config.GroupVersion.String())
	}
	if called != 0 {
		t.Fatalf("Expected not be called again getting a config from cache, was called %d additional times", called)
	}

	// Call for removed version, return error
	called = 0
	if _, err := clients.ClientConfigForVersion(&unversioned.GroupVersion{Version: "v1beta3"}); err == nil {
		t.Fatalf("Unexpected non-error: %v", err)
	}
	if called != 1 {
		t.Fatalf("Expected to be called once getting a config for a new version, was called %d times", called)
	}
}

func TestComputeDiscoverCacheDir(t *testing.T) {
	testCases := []struct {
		name      string
		parentDir string
		host      string

		expected string
	}{
		{
			name:      "simple append",
			parentDir: "~/",
			host:      "localhost:8443",
			expected:  "~/localhost_8443",
		},
		{
			name:      "with path",
			parentDir: "~/",
			host:      "localhost:8443/prefix",
			expected:  "~/localhost_8443/prefix",
		},
		{
			name:      "dotted name",
			parentDir: "~/",
			host:      "mine.example.org:8443",
			expected:  "~/mine.example.org_8443",
		},
		{
			name:      "IP",
			parentDir: "~/",
			host:      "127.0.0.1:8443",
			expected:  "~/127.0.0.1_8443",
		},
		{
			// restricted characters from: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#naming_conventions
			// it's not a complete list because they have a very helpful: "Any other character that the target file system does not allow."
			name:      "windows safe",
			parentDir: "~/",
			host:      `<>:"\|?*`,
			expected:  "~/________",
		},
	}

	for _, tc := range testCases {
		actual := computeDiscoverCacheDir(tc.parentDir, tc.host)
		if actual != tc.expected {
			t.Errorf("%s: expected %v, got %v", tc.name, tc.expected, actual)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/u-root/u-root/pkg/wpa/passphrase"
)

const (
	cmd          = "wifi [options] essid [passphrase] [identity]"
	nopassphrase = `network={
		ssid="%s"
		proto=RSN
		key_mgmt=NONE
	}`
	eap = `network={
		ssid="%s"
		key_mgmt=WPA-EAP
		identity="%s"
		password="%s"
	}`
)

var (
	// flags
	iface = flag.String("i", "wlan0", "interface to use")
	list  = flag.Bool("l", false, "list all nearby WiFi")
	test  = flag.Bool("test", false, "set up a test server")

	// RegEx for parsing iwlist output
	CellRE       = regexp.MustCompile("(?m)^\\s*Cell")
	EssidRE      = regexp.MustCompile("(?m)^\\s*ESSID.*")
	EncKeyOptRE  = regexp.MustCompile("(?m)^\\s*Encryption key:(on|off)$")
	Wpa2RE       = regexp.MustCompile("(?m)^\\s*IE: IEEE 802.11i/WPA2 Version 1$")
	AuthSuitesRE = regexp.MustCompile("(?m)^\\s*Authentication Suites .*$")

	// State of the service
	CurEssid        string
	ConnectingEssid string
	NearbyWifis     []WifiOption

	// State for the Test Server
	StubNearbyWifis = []WifiOption{
		{"Stub1", NoEnc},
		{"Stub2", WpaPsk},
		{"Stub3", WpaEap},
		{"Stub4", NotSupportedProto},
	}
)

func init() {
	defUsage := flag.Usage
	flag.Usage = func() {
		os.Args[0] = cmd
		defUsage()
	}
}

func scanWifi() error {
	o, err := exec.Command("iwlist", *iface, "scanning").CombinedOutput()
	if err != nil {
		return fmt.Errorf("iwlist: %v (%v)", string(o), err)
	}
	NearbyWifis = parseIwlistOut(o)
	return nil
}

func getState() State {
	return State{NearbyWifis, ConnectingEssid, CurEssid}
}

/*
 * Assumptions:
 *	1) Cell, essid, and encryption key option are 1:1 match
 *	2) We only support IEEE 802.11i/WPA2 Version 1
 *	3) Each WiFi only support (1) authentication suites (based on observations)
 */

func parseIwlistOut(o []byte) []WifiOption {
	cells := CellRE.FindAllIndex(o, -1)
	essids := EssidRE.FindAll(o, -1)
	encKeyOpts := EncKeyOptRE.FindAll(o, -1)

	if cells == nil {
		return nil
	}

	var res []WifiOption
	knownEssids := make(map[string]bool)

	// Assemble all the WiFi options
	for i := 0; i < len(cells); i++ {
		essid := strings.Trim(strings.Split(string(essids[i]), ":")[1], "\"\n")
		if knownEssids[essid] {
			continue
		}
		knownEssids[essid] = true
		encKeyOpt := strings.Trim(strings.Split(string(encKeyOpts[i]), ":")[1], "\n")
		if encKeyOpt == "off" {
			res = append(res, WifiOption{essid, NoEnc})
			continue
		}
		// Find the proper Authentication Suites
		start, end := cells[i][0], len(o)
		if i != len(cells)-1 {
			end = cells[i+1][0]
		}
		// Narrow down the scope when looking for WPA Tag
		wpa2SearchArea := o[start:end]
		l := Wpa2RE.FindIndex(wpa2SearchArea)
		if l == nil {
			res = append(res, WifiOption{essid, NotSupportedProto})
			continue
		}
		// Narrow down the scope when looking for Authorization Suites
		authSearchArea := wpa2SearchArea[l[0]:]
		authSuites := strings.Trim(strings.Split(string(AuthSuitesRE.Find(authSearchArea)), ":")[1], "\n ")
		switch authSuites {
		case "PSK":
			res = append(res, WifiOption{essid, WpaPsk})
		case "802.1x":
			res = append(res, WifiOption{essid, WpaEap})
		default:
			res = append(res, WifiOption{essid, NotSupportedProto})
		}
	}
	return res
}

func generateConfig(a ...string) (conf []byte, err error) {
	// format of a: [essid, pass, id]
	switch {
	case len(a) == 3:
		conf = []byte(fmt.Sprintf(eap, a[0], a[2], a[1]))
	case len(a) == 2:
		conf, err = passphrase.Run(a[0], a[1])
		if err != nil {
			return nil, fmt.Errorf("essid: %v, pass: %v : %v", a[0], a[1], err)
		}
	case len(a) == 1:
		conf = []byte(fmt.Sprintf(nopassphrase, a[0]))
	default:
		return nil, fmt.Errorf("generateConfig needs 1, 2, or 3 args")
	}
	return
}

// To prevent race condition, there should be only one
// goroutine running connectWifiArbitrator at any one time
func connectWifiArbitrator() {
	for req := range ConnectReqChan {
		if ConnectingEssid != "" {
			req.c <- fmt.Errorf("Service is trying to connect to %s", ConnectingEssid)
		} else {
			ConnectingEssid = req.essid
			req.c <- nil
		}
	}
}

func connectWifi(a ...string) error {
	// format of a: [essid, pass, id]
	conf, err := generateConfig(a...)
	if err != nil {
		ConnectingEssid = ""
		return err
	}

	if err := ioutil.WriteFile("/tmp/wifi.conf", conf, 0444); err != nil {
		ConnectingEssid = ""
		return fmt.Errorf("/tmp/wifi.conf: %v", err)
	}

	c := make(chan error, 2)

	// There's no telling how long the supplicant will take, but on the other hand,
	// it's been almost instantaneous. But, further, it needs to keep running.
	go func() {
		if o, err := exec.Command("wpa_supplicant", "-i"+*iface, "-c/tmp/wifi.conf").CombinedOutput(); err != nil {
			c <- fmt.Errorf("wpa_supplicant: %v (%v)", string(o), err)
		} else {
			c <- nil
		}
	}()

	go func() {
		if o, err := exec.Command("./dhclient", "-ipv4=true", "-ipv6=false", "-verbose", *iface).CombinedOutput(); err != nil {
			c <- fmt.Errorf("dhclient: %v (%v)", string(o), err)
		} else {
			c <- nil
		}
	}()

	if errWpaSupplicant, errDhClient := <-c, <-c; errWpaSupplicant != nil || errDhClient != nil {
		ConnectingEssid = ""
		return fmt.Errorf("%v \n %v", errWpaSupplicant, errDhClient)
	}
	CurEssid = a[0]
	ConnectingEssid = ""
	return nil
}

func main() {
	flag.Parse()

	if *list {
		if err := scanWifi(); err != nil {
			log.Fatalf("error: %v", err)
		}
		for _, wifiOpt := range NearbyWifis {
			switch wifiOpt.AuthSuite {
			case NoEnc:
				fmt.Printf("%s: No Passphrase\n", wifiOpt.Essid)
			case WpaPsk:
				fmt.Printf("%s: WPA-PSK (only passphrase)\n", wifiOpt.Essid)
			case WpaEap:
				fmt.Printf("%s: WPA-EAP (passphrase and identity)\n", wifiOpt.Essid)
			case NotSupportedProto:
				fmt.Printf("%s: Not a supported protocol\n", wifiOpt.Essid)
			}
		}
		return
	}

	if *test {
		NearbyWifis = StubNearbyWifis
		go connectWifiArbitrator()
		startServer()
	}

	a := flag.Args()

	if len(a) == 0 {
		// Experimental Part
		go scanWifi()
		go connectWifiArbitrator()
		startServer()
		return
	}

	if err := connectWifi(a...); err != nil {
		flag.Usage()
		log.Fatalf("error: %v", err)
	}
}
<commit_msg>Output dhclient and wpa_supplicant onto terminal for an easier time debugging<commit_after>// Copyright 2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/u-root/u-root/pkg/wpa/passphrase"
)

const (
	cmd          = "wifi [options] essid [passphrase] [identity]"
	nopassphrase = `network={
		ssid="%s"
		proto=RSN
		key_mgmt=NONE
	}`
	eap = `network={
		ssid="%s"
		key_mgmt=WPA-EAP
		identity="%s"
		password="%s"
	}`
)

var (
	// flags
	iface = flag.String("i", "wlan0", "interface to use")
	list  = flag.Bool("l", false, "list all nearby WiFi")
	test  = flag.Bool("test", false, "set up a test server")

	// RegEx for parsing iwlist output
	CellRE       = regexp.MustCompile("(?m)^\\s*Cell")
	EssidRE      = regexp.MustCompile("(?m)^\\s*ESSID.*")
	EncKeyOptRE  = regexp.MustCompile("(?m)^\\s*Encryption key:(on|off)$")
	Wpa2RE       = regexp.MustCompile("(?m)^\\s*IE: IEEE 802.11i/WPA2 Version 1$")
	AuthSuitesRE = regexp.MustCompile("(?m)^\\s*Authentication Suites .*$")

	// State of the service
	CurEssid        string
	ConnectingEssid string
	NearbyWifis     []WifiOption

	// State for the Test Server
	StubNearbyWifis = []WifiOption{
		{"Stub1", NoEnc},
		{"Stub2", WpaPsk},
		{"Stub3", WpaEap},
		{"Stub4", NotSupportedProto},
	}
)

func init() {
	defUsage := flag.Usage
	flag.Usage = func() {
		os.Args[0] = cmd
		defUsage()
	}
}

func scanWifi() error {
	o, err := exec.Command("iwlist", *iface, "scanning").CombinedOutput()
	if err != nil {
		return fmt.Errorf("iwlist: %v (%v)", string(o), err)
	}
	NearbyWifis = parseIwlistOut(o)
	return nil
}

func getState() State {
	return State{NearbyWifis, ConnectingEssid, CurEssid}
}

/*
 * Assumptions:
 *	1) Cell, essid, and encryption key option are 1:1 match
 *	2) We only support IEEE 802.11i/WPA2 Version 1
 *	3) Each WiFi only support (1) authentication suites (based on observations)
 */

func parseIwlistOut(o []byte) []WifiOption {
	cells := CellRE.FindAllIndex(o, -1)
	essids := EssidRE.FindAll(o, -1)
	encKeyOpts := EncKeyOptRE.FindAll(o, -1)

	if cells == nil {
		return nil
	}

	var res []WifiOption
	knownEssids := make(map[string]bool)

	// Assemble all the WiFi options
	for i := 0; i < len(cells); i++ {
		essid := strings.Trim(strings.Split(string(essids[i]), ":")[1], "\"\n")
		if knownEssids[essid] {
			continue
		}
		knownEssids[essid] = true
		encKeyOpt := strings.Trim(strings.Split(string(encKeyOpts[i]), ":")[1], "\n")
		if encKeyOpt == "off" {
			res = append(res, WifiOption{essid, NoEnc})
			continue
		}
		// Find the proper Authentication Suites
		start, end := cells[i][0], len(o)
		if i != len(cells)-1 {
			end = cells[i+1][0]
		}
		// Narrow down the scope when looking for WPA Tag
		wpa2SearchArea := o[start:end]
		l := Wpa2RE.FindIndex(wpa2SearchArea)
		if l == nil {
			res = append(res, WifiOption{essid, NotSupportedProto})
			continue
		}
		// Narrow down the scope when looking for Authorization Suites
		authSearchArea := wpa2SearchArea[l[0]:]
		authSuites := strings.Trim(strings.Split(string(AuthSuitesRE.Find(authSearchArea)), ":")[1], "\n ")
		switch authSuites {
		case "PSK":
			res = append(res, WifiOption{essid, WpaPsk})
		case "802.1x":
			res = append(res, WifiOption{essid, WpaEap})
		default:
			res = append(res, WifiOption{essid, NotSupportedProto})
		}
	}
	return res
}

func generateConfig(a ...string) (conf []byte, err error) {
	// format of a: [essid, pass, id]
	switch {
	case len(a) == 3:
		conf = []byte(fmt.Sprintf(eap, a[0], a[2], a[1]))
	case len(a) == 2:
		conf, err = passphrase.Run(a[0], a[1])
		if err != nil {
			return nil, fmt.Errorf("essid: %v, pass: %v : %v", a[0], a[1], err)
		}
	case len(a) == 1:
		conf = []byte(fmt.Sprintf(nopassphrase, a[0]))
	default:
		return nil, fmt.Errorf("generateConfig needs 1, 2, or 3 args")
	}
	return
}

// To prevent race condition, there should be only one
// goroutine running connectWifiArbitrator at any one time
func connectWifiArbitrator() {
	for req := range ConnectReqChan {
		if ConnectingEssid != "" {
			req.c <- fmt.Errorf("Service is trying to connect to %s", ConnectingEssid)
		} else {
			ConnectingEssid = req.essid
			req.c <- nil
		}
	}
}

func connectWifi(a ...string) error {
	// format of a: [essid, pass, id]
	conf, err := generateConfig(a...)
	if err != nil {
		ConnectingEssid = ""
		return err
	}

	if err := ioutil.WriteFile("/tmp/wifi.conf", conf, 0444); err != nil {
		ConnectingEssid = ""
		return fmt.Errorf("/tmp/wifi.conf: %v", err)
	}

	c := make(chan error, 2)

	// There's no telling how long the supplicant will take, but on the other hand,
	// it's been almost instantaneous. But, further, it needs to keep running.
	go func() {
		cmd := exec.Command("wpa_supplicant", "-i"+*iface, "-c/tmp/wifi.conf")
		cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr //For an easier time debugging
		if err := cmd.Run(); err != nil {
			c <- fmt.Errorf("wpa_supplicant error: %v", err)
		} else {
			c <- nil
		}
	}()

	go func() {
		cmd := exec.Command("dhclient", "-ipv4=true", "-ipv6=false", "-verbose", *iface)
		cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr //For an easier time debugging
		if err := cmd.Run(); err != nil {
			c <- fmt.Errorf("dhclient error: %v", err)
		} else {
			c <- nil
		}
	}()

	if errWpaSupplicant, errDhClient := <-c, <-c; errWpaSupplicant != nil || errDhClient != nil {
		ConnectingEssid = ""
		return fmt.Errorf("%v \n %v", errWpaSupplicant, errDhClient)
	}
	CurEssid = a[0]
	ConnectingEssid = ""
	return nil
}

func main() {
	flag.Parse()

	if *list {
		if err := scanWifi(); err != nil {
			log.Fatalf("error: %v", err)
		}
		for _, wifiOpt := range NearbyWifis {
			switch wifiOpt.AuthSuite {
			case NoEnc:
				fmt.Printf("%s: No Passphrase\n", wifiOpt.Essid)
			case WpaPsk:
				fmt.Printf("%s: WPA-PSK (only passphrase)\n", wifiOpt.Essid)
			case WpaEap:
				fmt.Printf("%s: WPA-EAP (passphrase and identity)\n", wifiOpt.Essid)
			case NotSupportedProto:
				fmt.Printf("%s: Not a supported protocol\n", wifiOpt.Essid)
			}
		}
		return
	}

	if *test {
		NearbyWifis = StubNearbyWifis
		go connectWifiArbitrator()
		startServer()
	}

	a := flag.Args()

	if len(a) == 0 {
		// Experimental Part
		go scanWifi()
		go connectWifiArbitrator()
		startServer()
		return
	}

	if err := connectWifi(a...); err != nil {
		flag.Usage()
		log.Fatalf("error: %v", err)
	}
}
<|endoftext|>


<commit_before>//
// Copyright 2022 The GUAC Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package spdx

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/guacsec/guac/pkg/assembler"
	"github.com/guacsec/guac/pkg/handler/processor"
	"github.com/guacsec/guac/pkg/logging"
	spdx_json "github.com/spdx/tools-golang/json"
	spdx_common "github.com/spdx/tools-golang/spdx/common"
	"github.com/spdx/tools-golang/spdx/v2_2"
)

type spdxParser struct {
	packages map[string][]assembler.PackageNode
	files    map[string][]assembler.ArtifactNode
	spdxDoc  *v2_2.Document
}

func NewSpdxParser() *spdxParser {
	return &spdxParser{
		packages: map[string][]assembler.PackageNode{},
		files:    map[string][]assembler.ArtifactNode{},
	}
}

func (s *spdxParser) Parse(ctx context.Context, doc *processor.Document) error {
	spdxDoc, err := parseSpdxBlob(doc.Blob)
	if err != nil {
		return fmt.Errorf("failed to parse SPDX document: %w", err)
	}
	s.spdxDoc = spdxDoc
	s.getPackages()
	s.getFiles()
	return nil
}

// creating top level package manually until https://github.com/anchore/syft/issues/1241 is resolved
func (s *spdxParser) getTopLevelPackage() {
	// oci purl: pkg:oci/debian@sha256%3A244fd47e07d10?repository_url=ghcr.io/debian&tag=bullseye
	splitImage := strings.Split(s.spdxDoc.DocumentName, "/")
	if len(splitImage) == 3 {
		topPackage := assembler.PackageNode{}
		topPackage.Purl = "pkg:oci/" + splitImage[2] + "?repository_url=" + splitImage[0] + "/" + splitImage[1]
		topPackage.Name = s.spdxDoc.DocumentName
		s.packages[string(s.spdxDoc.SPDXIdentifier)] = append(s.packages[string(s.spdxDoc.SPDXIdentifier)], topPackage)
	}
}

func (s *spdxParser) getPackages() {
	s.getTopLevelPackage()
	for _, pac := range s.spdxDoc.Packages {
		currentPackage := assembler.PackageNode{}
		currentPackage.Name = pac.PackageName
		for _, ext := range pac.PackageExternalReferences {
			if strings.HasPrefix(ext.RefType, "cpe") {
				currentPackage.CPEs = append(currentPackage.CPEs, ext.Locator)
			} else if ext.RefType == spdx_common.TypePackageManagerPURL {
				currentPackage.Purl = ext.Locator
			}
		}
		for _, checksum := range pac.PackageChecksums {
			currentPackage.Digest = append(currentPackage.Digest, string(checksum.Algorithm)+":"+checksum.Value)
		}
		s.packages[string(pac.PackageSPDXIdentifier)] = append(s.packages[string(pac.PackageSPDXIdentifier)], currentPackage)
	}
}

func (s *spdxParser) getFiles() {
	for _, file := range s.spdxDoc.Files {
		currentFile := assembler.ArtifactNode{}
		for _, checksum := range file.Checksums {
			currentFile.Name = file.FileName
			currentFile.Digest = string(checksum.Algorithm) + ":" + checksum.Value
			s.files[string(file.FileSPDXIdentifier)] = append(s.files[string(file.FileSPDXIdentifier)], currentFile)
		}
	}
}

func parseSpdxBlob(p []byte) (*v2_2.Document, error) {
	reader := bytes.NewReader(p)
	spdx, err := spdx_json.Load2_2(reader)
	if err != nil {
		return nil, err
	}
	return spdx, nil
}

func (s *spdxParser) CreateNodes(ctx context.Context) []assembler.GuacNode {
	nodes := []assembler.GuacNode{}
	for _, packNodes := range s.packages {
		for _, packNode := range packNodes {
			nodes = append(nodes, packNode)
		}
	}
	for _, fileNodes := range s.files {
		for _, fileNode := range fileNodes {
			nodes = append(nodes, fileNode)
		}
	}
	return nodes
}

func (s *spdxParser) getPackageElement(elementID string) []assembler.PackageNode {
	if packNode, ok := s.packages[string(elementID)]; ok {
		return packNode
	}
	return nil
}

func (s *spdxParser) getFileElement(elementID string) []assembler.ArtifactNode {
	if fileNode, ok := s.files[string(elementID)]; ok {
		return fileNode
	}
	return nil
}

func (s *spdxParser) CreateEdges(ctx context.Context, foundIdentities []assembler.IdentityNode) []assembler.GuacEdge {
	logger := logging.FromContext(ctx)
	edges := []assembler.GuacEdge{}
	toplevel := s.getPackageElement("SPDXRef-DOCUMENT")
	// adding top level package edge manually for all depends on package
	if toplevel != nil {
		edges = append(edges, createTopLevelEdges(toplevel[0], s.packages)...)
	}
	for _, rel := range s.spdxDoc.Relationships {
		foundPackNodes := s.getPackageElement("SPDXRef-" + string(rel.RefA.ElementRefID))
		foundFileNodes := s.getFileElement("SPDXRef-" + string(rel.RefA.ElementRefID))
		relatedPackNodes := s.getPackageElement("SPDXRef-" + string(rel.RefB.ElementRefID))
		relatedFileNodes := s.getFileElement("SPDXRef-" + string(rel.RefB.ElementRefID))
		for _, packNode := range foundPackNodes {
			createdEdge, err := getEdge(packNode, rel.Relationship, relatedPackNodes, relatedFileNodes)
			if err != nil {
				logger.Errorf("error generating spdx edge %v", err)
				continue
			}
			if createdEdge != nil {
				edges = append(edges, createdEdge)
			}
		}
		for _, fileNode := range foundFileNodes {
			createdEdge, err := getEdge(fileNode, rel.Relationship, relatedPackNodes, relatedFileNodes)
			if err != nil {
				logger.Errorf("error generating spdx edge %v", err)
				continue
			}
			if createdEdge != nil {
				edges = append(edges, createdEdge)
			}
		}
	}
	return edges
}

func createTopLevelEdges(toplevel assembler.PackageNode, packages map[string][]assembler.PackageNode) []assembler.GuacEdge {
	edges := []assembler.GuacEdge{}
	for _, packNodes := range packages {
		for _, packNode := range packNodes {
			if packNode.Purl != toplevel.Purl {
				e := assembler.DependsOnEdge{
					PackageNode:       toplevel,
					PackageDependency: packNode,
				}
				edges = append(edges, e)
			}
		}
	}
	return edges
}

func getEdge(foundNode assembler.GuacNode, relationship string, relatedPackNodes []assembler.PackageNode, relatedFileNodes []assembler.ArtifactNode) (assembler.GuacEdge, error) {
	if len(relatedFileNodes) > 0 {
		for _, rfileNode := range relatedFileNodes {
			return getEdgeByType(relationship, foundNode, rfileNode)
		}
	} else if len(relatedPackNodes) > 0 {
		for _, rpackNode := range relatedPackNodes {
			return getEdgeByType(relationship, foundNode, rpackNode)
		}
	}
	return nil, nil
}

func getEdgeByType(relationship string, foundNode assembler.GuacNode, relatedNode assembler.GuacNode) (assembler.GuacEdge, error) {
	switch relationship {
	case spdx_common.TypeRelationshipContains:
		return getContainsEdge(foundNode, relatedNode)
	case spdx_common.TypeRelationshipDependsOn:
		return getDependsOnEdge(foundNode, relatedNode), nil
	}
	return nil, nil
}

func getContainsEdge(foundNode assembler.GuacNode, relatedNode assembler.GuacNode) (assembler.GuacEdge, error) {
	e := assembler.ContainsEdge{}
	if foundNode.Type() == "Package" {
		e.PackageNode = foundNode.(assembler.PackageNode)
	} else {
		return nil, errors.New("node type mismatch during contains edge creation")
	}
	if relatedNode.Type() == "Artifact" {
		e.ContainedArtifact = relatedNode.(assembler.ArtifactNode)
	} else {
		return nil, errors.New("node type mismatch during contains edge creation")
	}
	return e, nil
}

func getDependsOnEdge(foundNode assembler.GuacNode, relatedNode assembler.GuacNode) assembler.GuacEdge {
	e := assembler.DependsOnEdge{}
	if foundNode.Type() == "Package" {
		e.PackageNode = foundNode.(assembler.PackageNode)
	} else {
		e.ArtifactNode = foundNode.(assembler.ArtifactNode)
	}

	if relatedNode.Type() == "Package" {
		e.PackageDependency = relatedNode.(assembler.PackageNode)
	} else {
		e.ArtifactDependency = relatedNode.(assembler.ArtifactNode)
	}
	return e
}

func (s *spdxParser) GetIdentities(ctx context.Context) []assembler.IdentityNode {
	return nil
}

func (s *spdxParser) GetDocType() processor.DocumentType {
	return processor.DocumentSPDX
}
<commit_msg>minor spdx bug fixes for heuristics (#146)<commit_after>//
// Copyright 2022 The GUAC Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package spdx

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/guacsec/guac/pkg/assembler"
	"github.com/guacsec/guac/pkg/handler/processor"
	"github.com/guacsec/guac/pkg/logging"
	spdx_json "github.com/spdx/tools-golang/json"
	spdx_common "github.com/spdx/tools-golang/spdx/common"
	"github.com/spdx/tools-golang/spdx/v2_2"
)

type spdxParser struct {
	packages map[string][]assembler.PackageNode
	files    map[string][]assembler.ArtifactNode
	spdxDoc  *v2_2.Document
}

func NewSpdxParser() *spdxParser {
	return &spdxParser{
		packages: map[string][]assembler.PackageNode{},
		files:    map[string][]assembler.ArtifactNode{},
	}
}

func (s *spdxParser) Parse(ctx context.Context, doc *processor.Document) error {
	spdxDoc, err := parseSpdxBlob(doc.Blob)
	if err != nil {
		return fmt.Errorf("failed to parse SPDX document: %w", err)
	}
	s.spdxDoc = spdxDoc
	s.getPackages()
	s.getFiles()
	return nil
}

// creating top level package manually until https://github.com/anchore/syft/issues/1241 is resolved
func (s *spdxParser) getTopLevelPackage() {
	// oci purl: pkg:oci/debian@sha256%3A244fd47e07d10?repository_url=ghcr.io/debian&tag=bullseye
	splitImage := strings.Split(s.spdxDoc.DocumentName, "/")
	if len(splitImage) == 3 {
		topPackage := assembler.PackageNode{}
		topPackage.Purl = "pkg:oci/" + splitImage[2] + "?repository_url=" + splitImage[0] + "/" + splitImage[1]
		topPackage.Name = s.spdxDoc.DocumentName
		s.packages[string(s.spdxDoc.SPDXIdentifier)] = append(s.packages[string(s.spdxDoc.SPDXIdentifier)], topPackage)
	} else if len(splitImage) == 2 {
		topPackage := assembler.PackageNode{}
		topPackage.Purl = "pkg:oci/" + splitImage[1] + "?repository_url=" + splitImage[0]
		topPackage.Name = s.spdxDoc.DocumentName
		s.packages[string(s.spdxDoc.SPDXIdentifier)] = append(s.packages[string(s.spdxDoc.SPDXIdentifier)], topPackage)
	}
}

func (s *spdxParser) getPackages() {
	s.getTopLevelPackage()
	for _, pac := range s.spdxDoc.Packages {
		currentPackage := assembler.PackageNode{}
		currentPackage.Name = pac.PackageName
		for _, ext := range pac.PackageExternalReferences {
			if strings.HasPrefix(ext.RefType, "cpe") {
				currentPackage.CPEs = append(currentPackage.CPEs, ext.Locator)
			} else if ext.RefType == spdx_common.TypePackageManagerPURL {
				currentPackage.Purl = ext.Locator
			}
		}
		for _, checksum := range pac.PackageChecksums {
			currentPackage.Digest = append(currentPackage.Digest, strings.ToLower(string(checksum.Algorithm))+":"+checksum.Value)
		}
		s.packages[string(pac.PackageSPDXIdentifier)] = append(s.packages[string(pac.PackageSPDXIdentifier)], currentPackage)
	}
}

func (s *spdxParser) getFiles() {
	for _, file := range s.spdxDoc.Files {
		currentFile := assembler.ArtifactNode{}
		for _, checksum := range file.Checksums {
			currentFile.Name = file.FileName
			currentFile.Digest = strings.ToLower(string(checksum.Algorithm)) + ":" + checksum.Value
			s.files[string(file.FileSPDXIdentifier)] = append(s.files[string(file.FileSPDXIdentifier)], currentFile)
		}
	}
}

func parseSpdxBlob(p []byte) (*v2_2.Document, error) {
	reader := bytes.NewReader(p)
	spdx, err := spdx_json.Load2_2(reader)
	if err != nil {
		return nil, err
	}
	return spdx, nil
}

func (s *spdxParser) CreateNodes(ctx context.Context) []assembler.GuacNode {
	nodes := []assembler.GuacNode{}
	for _, packNodes := range s.packages {
		for _, packNode := range packNodes {
			nodes = append(nodes, packNode)
		}
	}
	for _, fileNodes := range s.files {
		for _, fileNode := range fileNodes {
			nodes = append(nodes, fileNode)
		}
	}
	return nodes
}

func (s *spdxParser) getPackageElement(elementID string) []assembler.PackageNode {
	if packNode, ok := s.packages[string(elementID)]; ok {
		return packNode
	}
	return nil
}

func (s *spdxParser) getFileElement(elementID string) []assembler.ArtifactNode {
	if fileNode, ok := s.files[string(elementID)]; ok {
		return fileNode
	}
	return nil
}

func (s *spdxParser) CreateEdges(ctx context.Context, foundIdentities []assembler.IdentityNode) []assembler.GuacEdge {
	logger := logging.FromContext(ctx)
	edges := []assembler.GuacEdge{}
	toplevel := s.getPackageElement("SPDXRef-DOCUMENT")
	// adding top level package edge manually for all depends on package
	if toplevel != nil {
		edges = append(edges, createTopLevelEdges(toplevel[0], s.packages, s.files)...)
	}
	for _, rel := range s.spdxDoc.Relationships {
		foundPackNodes := s.getPackageElement("SPDXRef-" + string(rel.RefA.ElementRefID))
		foundFileNodes := s.getFileElement("SPDXRef-" + string(rel.RefA.ElementRefID))
		relatedPackNodes := s.getPackageElement("SPDXRef-" + string(rel.RefB.ElementRefID))
		relatedFileNodes := s.getFileElement("SPDXRef-" + string(rel.RefB.ElementRefID))
		for _, packNode := range foundPackNodes {
			createdEdge, err := getEdge(packNode, rel.Relationship, relatedPackNodes, relatedFileNodes)
			if err != nil {
				logger.Errorf("error generating spdx edge %v", err)
				continue
			}
			if createdEdge != nil {
				edges = append(edges, createdEdge)
			}
		}
		for _, fileNode := range foundFileNodes {
			createdEdge, err := getEdge(fileNode, rel.Relationship, relatedPackNodes, relatedFileNodes)
			if err != nil {
				logger.Errorf("error generating spdx edge %v", err)
				continue
			}
			if createdEdge != nil {
				edges = append(edges, createdEdge)
			}
		}
	}
	return edges
}

func createTopLevelEdges(toplevel assembler.PackageNode, packages map[string][]assembler.PackageNode, files map[string][]assembler.ArtifactNode) []assembler.GuacEdge {
	edges := []assembler.GuacEdge{}
	for _, packNodes := range packages {
		for _, packNode := range packNodes {
			if packNode.Purl != toplevel.Purl {
				e := assembler.DependsOnEdge{
					PackageNode:       toplevel,
					PackageDependency: packNode,
				}
				edges = append(edges, e)
			}
		}
	}

	for _, fileNodes := range files {
		for _, fileNode := range fileNodes {
			e := assembler.DependsOnEdge{
				PackageNode:        toplevel,
				ArtifactDependency: fileNode,
			}
			edges = append(edges, e)
		}
	}

	return edges
}

func getEdge(foundNode assembler.GuacNode, relationship string, relatedPackNodes []assembler.PackageNode, relatedFileNodes []assembler.ArtifactNode) (assembler.GuacEdge, error) {
	if len(relatedFileNodes) > 0 {
		for _, rfileNode := range relatedFileNodes {
			return getEdgeByType(relationship, foundNode, rfileNode)
		}
	} else if len(relatedPackNodes) > 0 {
		for _, rpackNode := range relatedPackNodes {
			return getEdgeByType(relationship, foundNode, rpackNode)
		}
	}
	return nil, nil
}

func getEdgeByType(relationship string, foundNode assembler.GuacNode, relatedNode assembler.GuacNode) (assembler.GuacEdge, error) {
	switch relationship {
	case spdx_common.TypeRelationshipContains:
		return getContainsEdge(foundNode, relatedNode)
	case spdx_common.TypeRelationshipDependsOn:
		return getDependsOnEdge(foundNode, relatedNode), nil
	}
	return nil, nil
}

func getContainsEdge(foundNode assembler.GuacNode, relatedNode assembler.GuacNode) (assembler.GuacEdge, error) {
	e := assembler.ContainsEdge{}
	if foundNode.Type() == "Package" {
		e.PackageNode = foundNode.(assembler.PackageNode)
	} else {
		return nil, errors.New("node type mismatch during contains edge creation")
	}
	if relatedNode.Type() == "Artifact" {
		e.ContainedArtifact = relatedNode.(assembler.ArtifactNode)
	} else {
		return nil, errors.New("node type mismatch during contains edge creation")
	}
	return e, nil
}

func getDependsOnEdge(foundNode assembler.GuacNode, relatedNode assembler.GuacNode) assembler.GuacEdge {
	e := assembler.DependsOnEdge{}
	if foundNode.Type() == "Package" {
		e.PackageNode = foundNode.(assembler.PackageNode)
	} else {
		e.ArtifactNode = foundNode.(assembler.ArtifactNode)
	}

	if relatedNode.Type() == "Package" {
		e.PackageDependency = relatedNode.(assembler.PackageNode)
	} else {
		e.ArtifactDependency = relatedNode.(assembler.ArtifactNode)
	}
	return e
}

func (s *spdxParser) GetIdentities(ctx context.Context) []assembler.IdentityNode {
	return nil
}

func (s *spdxParser) GetDocType() processor.DocumentType {
	return processor.DocumentSPDX
}
<|endoftext|>