diff --git a/Dockerfile b/Dockerfile index e6ae467a4..cc7900d87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Copyright 2019 Jason Ertel (jertel). All rights reserved. -# Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +# Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. # # This program is distributed under the terms of version 2 of the # GNU General Public License. See LICENSE for further details. diff --git a/Dockerfile.kratos b/Dockerfile.kratos index 748c680dc..109d21a42 100644 --- a/Dockerfile.kratos +++ b/Dockerfile.kratos @@ -1,5 +1,5 @@ # Copyright 2019 Jason Ertel (jertel). All rights reserved. -# Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +# Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. # # This program is distributed under the terms of version 2 of the # GNU General Public License. See LICENSE for further details. diff --git a/agent/agent.go b/agent/agent.go index a6835d56a..f459c894f 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -17,19 +17,19 @@ import ( ) type Agent struct { - Client *web.Client - Config *config.AgentConfig - JobMgr *JobManager - stoppedChan chan bool - Version string + Client *web.Client + Config *config.AgentConfig + JobMgr *JobManager + stoppedChan chan bool + Version string } func NewAgent(cfg *config.AgentConfig, version string) *Agent { agent := &Agent{ - Config: cfg, - Client: web.NewClient(cfg.ServerUrl, cfg.VerifyCert), + Config: cfg, + Client: web.NewClient(cfg.ServerUrl, cfg.VerifyCert), stoppedChan: make(chan bool, 1), - Version: version, + Version: version, } agent.JobMgr = NewJobManager(agent) return agent @@ -47,5 +47,5 @@ func (agent *Agent) Stop() { } func (agent *Agent) Wait() { - <- agent.stoppedChan -} \ No newline at end of file + <-agent.stoppedChan +} diff --git a/agent/agent_test.go b/agent/agent_test.go index 60e98eb4a..a49153918 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. diff --git a/agent/jobmanager.go b/agent/jobmanager.go index 9a60693c8..d47c384db 100644 --- a/agent/jobmanager.go +++ b/agent/jobmanager.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -12,12 +12,12 @@ package agent import ( "errors" + "github.com/apex/log" + "github.com/security-onion-solutions/securityonion-soc/model" "io" "strconv" "sync" "time" - "github.com/apex/log" - "github.com/security-onion-solutions/securityonion-soc/model" ) type JobManager struct { @@ -31,10 +31,10 @@ type JobManager struct { func NewJobManager(agent *Agent) *JobManager { mgr := &JobManager{ agent: agent, - node: model.NewNode(agent.Config.NodeId), + node: model.NewNode(agent.Config.NodeId), } - // Any field/value added to this list must be manually copied to the + // Any field/value added to this list must be manually copied to the // existing node object in filedatastoreimpl.go::UpdateNode() mgr.node.Role = agent.Config.Role mgr.node.Description = agent.Config.Description @@ -59,7 +59,7 @@ func (mgr *JobManager) Start() { } else { log.WithField("jobId", job.Id).Info("Discovered pending job") var reader io.ReadCloser - reader, err = mgr.ProcessJob(job) + reader, err = mgr.ProcessJob(job) if err == nil { if reader != nil { defer reader.Close() @@ -104,7 +104,7 @@ func (mgr *JobManager) ProcessJob(job *model.Job) (io.ReadCloser, error) { for _, processor := range mgr.jobProcessors { reader, err = processor.ProcessJob(job, reader) if err != nil { - log.WithError(err).WithFields(log.Fields { + log.WithError(err).WithFields(log.Fields{ "jobId": job.Id, }).Error("Failed to process job; job processing aborted") break @@ -115,7 +115,7 @@ func (mgr *JobManager) ProcessJob(job *model.Job) (io.ReadCloser, error) { func (mgr *JobManager) CleanupJob(job *model.Job) { for _, processor := range mgr.jobProcessors { - processor.CleanupJob(job) + processor.CleanupJob(job) } } @@ -131,7 +131,7 @@ func (mgr *JobManager) updateDataEpoch() { } func (mgr *JobManager) StreamJobResults(job *model.Job, reader io.ReadCloser) error { - resp, err := mgr.agent.Client.SendAuthorizedRequest("POST", "/api/stream?jobId=" + strconv.Itoa(job.Id), "application/octet-stream", reader) + resp, err := mgr.agent.Client.SendAuthorizedRequest("POST", "/api/stream?jobId="+strconv.Itoa(job.Id), "application/octet-stream", reader) if resp.StatusCode != 200 { err = errors.New("Unable to submit job results (" + strconv.Itoa(resp.StatusCode) + "): " + resp.Status) } diff --git a/agent/jobprocessor.go b/agent/jobprocessor.go index 8289616e6..e87a69f27 100644 --- a/agent/jobprocessor.go +++ b/agent/jobprocessor.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -11,13 +11,13 @@ package agent import ( + "github.com/security-onion-solutions/securityonion-soc/model" "io" "time" - "github.com/security-onion-solutions/securityonion-soc/model" ) type JobProcessor interface { ProcessJob(*model.Job, io.ReadCloser) (io.ReadCloser, error) CleanupJob(*model.Job) GetDataEpoch() time.Time -} \ No newline at end of file +} diff --git a/cmd/sensoroni.go b/cmd/sensoroni.go index e1a0b9033..c9b341c82 100644 --- a/cmd/sensoroni.go +++ b/cmd/sensoroni.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -13,10 +13,6 @@ package main import ( "flag" "fmt" - "os" - "os/signal" - "syscall" - "time" "github.com/apex/log" "github.com/apex/log/handlers/logfmt" "github.com/apex/log/handlers/text" @@ -26,6 +22,10 @@ import ( "github.com/security-onion-solutions/securityonion-soc/module" "github.com/security-onion-solutions/securityonion-soc/server" serverModules "github.com/security-onion-solutions/securityonion-soc/server/modules" + "os" + "os/signal" + "syscall" + "time" ) var ( @@ -58,10 +58,10 @@ func main() { logFile, _ := InitLogging(cfg.LogFilename, cfg.LogLevel) defer logFile.Close() - log.WithFields(log.Fields { - "version": cfg.Version, + log.WithFields(log.Fields{ + "version": cfg.Version, "buildTime": cfg.BuildTime, - }).Info("Version Information") + }).Info("Version Information") moduleMgr := module.NewModuleManager() var srv *server.Server diff --git a/cmd/sensoroni_test.go b/cmd/sensoroni_test.go index cbd34a50a..6495b3af4 100644 --- a/cmd/sensoroni_test.go +++ b/cmd/sensoroni_test.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. diff --git a/config/agentconfig.go b/config/agentconfig.go index bd35ae050..b92198461 100644 --- a/config/agentconfig.go +++ b/config/agentconfig.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -12,23 +12,23 @@ package config import ( "errors" - "os" "github.com/security-onion-solutions/securityonion-soc/module" + "os" ) const DEFAULT_POLL_INTERVAL_MS = 1000 type AgentConfig struct { - NodeId string `json:"nodeId"` - Role string `json:"role"` - Description string `json:"description"` - Address string `json:"address"` - Model string `json:"model"` - ServerUrl string `json:"serverUrl"` - VerifyCert bool `json:"verifyCert"` - PollIntervalMs int `json:"pollIntervalMs"` - Modules module.ModuleConfigMap `json:"modules"` - ModuleFailuresIgnored bool `json:"moduleFailuresIgnored"` + NodeId string `json:"nodeId"` + Role string `json:"role"` + Description string `json:"description"` + Address string `json:"address"` + Model string `json:"model"` + ServerUrl string `json:"serverUrl"` + VerifyCert bool `json:"verifyCert"` + PollIntervalMs int `json:"pollIntervalMs"` + Modules module.ModuleConfigMap `json:"modules"` + ModuleFailuresIgnored bool `json:"moduleFailuresIgnored"` } func (config *AgentConfig) Verify() error { @@ -43,4 +43,4 @@ func (config *AgentConfig) Verify() error { err = errors.New("Agent.ServerUrl configuration value is required") } return err -} \ No newline at end of file +} diff --git a/config/agentconfig_test.go b/config/agentconfig_test.go index 9e1a2e887..371aefc64 100644 --- a/config/agentconfig_test.go +++ b/config/agentconfig_test.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. diff --git a/config/clientparameters.go b/config/clientparameters.go index 0fcc73c1b..367a18f72 100644 --- a/config/clientparameters.go +++ b/config/clientparameters.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -19,6 +19,8 @@ const DEFAULT_MOST_RECENTLY_USED_LIMIT = 5 type ClientParameters struct { HuntingParams HuntingParameters `json:"hunt"` AlertingParams HuntingParameters `json:"alerts"` + CasesParams HuntingParameters `json:"cases"` + CaseParams CaseParameters `json:"case"` JobParams HuntingParameters `json:"job"` DocsUrl string `json:"docsUrl"` CheatsheetUrl string `json:"cheatsheetUrl"` @@ -30,6 +32,7 @@ type ClientParameters struct { CacheExpirationMs int `json:"cacheExpirationMs"` InactiveTools []string `json:"inactiveTools"` Tools []ClientTool `json:"tools"` + CasesEnabled bool `json:"casesEnabled"` } func (config *ClientParameters) Verify() error { @@ -39,6 +42,9 @@ func (config *ClientParameters) Verify() error { if err := config.AlertingParams.Verify(); err != nil { return err } + if err := config.CasesParams.Verify(); err != nil { + return err + } return config.JobParams.Verify() } @@ -70,6 +76,7 @@ type HuntingAction struct { Method string `json:"method"` Body string `json:"body"` Options map[string]interface{} `json:"options"` + Categories []string `json:"categories"` } type ToggleFilter struct { @@ -82,24 +89,24 @@ type ToggleFilter struct { } type HuntingParameters struct { - GroupItemsPerPage int `json:"groupItemsPerPage"` - GroupFetchLimit int `json:"groupFetchLimit"` - EventItemsPerPage int `json:"eventItemsPerPage"` - EventFetchLimit int `json:"eventFetchLimit"` - RelativeTimeValue int `json:"relativeTimeValue"` - RelativeTimeUnit int `json:"relativeTimeUnit"` - MostRecentlyUsedLimit int `json:"mostRecentlyUsedLimit"` - EventFields map[string][]string `json:"eventFields"` - QueryBaseFilter string `json:"queryBaseFilter"` - QueryToggleFilters []*ToggleFilter `json:"queryToggleFilters"` - Queries []*HuntingQuery `json:"queries"` - Actions []*HuntingAction `json:"actions"` - Advanced bool `json:"advanced"` - AckEnabled bool `json:"ackEnabled"` - EscalateEnabled bool `json:"escalateEnabled"` -} - -type GridParameters struct { + GroupItemsPerPage int `json:"groupItemsPerPage"` + GroupFetchLimit int `json:"groupFetchLimit"` + EventItemsPerPage int `json:"eventItemsPerPage"` + EventFetchLimit int `json:"eventFetchLimit"` + RelativeTimeValue int `json:"relativeTimeValue"` + RelativeTimeUnit int `json:"relativeTimeUnit"` + MostRecentlyUsedLimit int `json:"mostRecentlyUsedLimit"` + EventFields map[string][]string `json:"eventFields"` + QueryBaseFilter string `json:"queryBaseFilter"` + QueryToggleFilters []*ToggleFilter `json:"queryToggleFilters"` + Queries []*HuntingQuery `json:"queries"` + Actions []*HuntingAction `json:"actions"` + Advanced bool `json:"advanced"` + AckEnabled bool `json:"ackEnabled"` + EscalateEnabled bool `json:"escalateEnabled"` + EscalateRelatedEventsEnabled bool `json:"escalateRelatedEventsEnabled"` + ViewEnabled bool `json:"viewEnabled"` + CreateLink string `json:"createLink"` } func (params *HuntingParameters) Verify() error { @@ -131,3 +138,25 @@ func (params *HuntingParameters) combineDeprecatedLinkIntoLinks() { } } } + +type PresetParameters struct { + Labels []string `json:"labels"` + CustomEnabled bool `json:"customEnabled"` +} + +type CaseParameters struct { + MostRecentlyUsedLimit int `json:"mostRecentlyUsedLimit"` + RenderAbbreviatedCount int `json:"renderAbbreviatedCount"` + Presets map[string]PresetParameters `json:"presets"` +} + +func (params *CaseParameters) Verify() error { + var err error + if params.MostRecentlyUsedLimit < 0 { + params.MostRecentlyUsedLimit = 0 + } + return err +} + +type GridParameters struct { +} diff --git a/config/clientparameters_test.go b/config/clientparameters_test.go index bf88961e0..711f3adc0 100644 --- a/config/clientparameters_test.go +++ b/config/clientparameters_test.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -19,23 +19,31 @@ import ( func TestVerifyClientParameters(tester *testing.T) { params := &ClientParameters{} err := params.Verify() - if assert.Nil(tester, err) { - assert.Zero(tester, params.WebSocketTimeoutMs) - assert.Zero(tester, params.TipTimeoutMs) - assert.Zero(tester, params.ApiTimeoutMs) - assert.Zero(tester, params.CacheExpirationMs) - } + assert.Nil(tester, err) + assert.Zero(tester, params.WebSocketTimeoutMs) + assert.Zero(tester, params.TipTimeoutMs) + assert.Zero(tester, params.ApiTimeoutMs) + assert.Zero(tester, params.CacheExpirationMs) + assert.False(tester, params.CasesEnabled) + verifyInitialHuntingParams(tester, ¶ms.HuntingParams) + verifyInitialHuntingParams(tester, ¶ms.AlertingParams) + verifyInitialHuntingParams(tester, ¶ms.CasesParams) } func TestVerifyHuntingParams(tester *testing.T) { params := &HuntingParameters{} err := params.Verify() - if assert.Nil(tester, err) { - assert.Equal(tester, DEFAULT_GROUP_FETCH_LIMIT, params.GroupFetchLimit) - assert.Equal(tester, DEFAULT_EVENT_FETCH_LIMIT, params.EventFetchLimit) - assert.Equal(tester, DEFAULT_RELATIVE_TIME_VALUE, params.RelativeTimeValue) - assert.Equal(tester, DEFAULT_RELATIVE_TIME_UNIT, params.RelativeTimeUnit) - } + assert.Nil(tester, err) + verifyInitialHuntingParams(tester, params) +} + +func verifyInitialHuntingParams(tester *testing.T, params *HuntingParameters) { + assert.Equal(tester, DEFAULT_GROUP_FETCH_LIMIT, params.GroupFetchLimit) + assert.Equal(tester, DEFAULT_EVENT_FETCH_LIMIT, params.EventFetchLimit) + assert.Equal(tester, DEFAULT_RELATIVE_TIME_VALUE, params.RelativeTimeValue) + assert.Equal(tester, DEFAULT_RELATIVE_TIME_UNIT, params.RelativeTimeUnit) + assert.Equal(tester, false, params.EscalateRelatedEventsEnabled) + assert.Equal(tester, false, params.EscalateEnabled) } func TestCombineEmptyDeprecatedLinkIntoEmptyLinks(tester *testing.T) { @@ -71,3 +79,11 @@ func TestCombineDeprecatedLinkIntoNonEmptyLinks(tester *testing.T) { assert.Len(tester, action.Links, 2) assert.Len(tester, action.Link, 0) } + +func TestVerifyCaseParams(tester *testing.T) { + params := &CaseParameters{} + params.MostRecentlyUsedLimit = -1 + err := params.Verify() + assert.Nil(tester, err) + assert.Equal(tester, params.MostRecentlyUsedLimit, 0) +} diff --git a/config/config.go b/config/config.go index e64749649..9d77c5d94 100644 --- a/config/config.go +++ b/config/config.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -11,30 +11,30 @@ package config import ( - "time" "github.com/security-onion-solutions/securityonion-soc/json" + "time" ) type Config struct { - Filename string - Version string - BuildTime time.Time - LoadTime time.Time - LogLevel string `json:"logLevel"` - LogFilename string `json:"logFilename"` - ShutdownGracePeriodMs int `json:"shutdownGracePeriodMs"` - Server *ServerConfig `json:"server"` - Agent *AgentConfig `json:"agent"` + Filename string + Version string + BuildTime time.Time + LoadTime time.Time + LogLevel string `json:"logLevel"` + LogFilename string `json:"logFilename"` + ShutdownGracePeriodMs int `json:"shutdownGracePeriodMs"` + Server *ServerConfig `json:"server"` + Agent *AgentConfig `json:"agent"` } func LoadConfig(filename string, version string, buildTime time.Time) (*Config, error) { cfg := &Config{ - Version: version, - BuildTime: buildTime, - Filename: filename, - LoadTime: time.Now(), - LogLevel: "info", - LogFilename: filename + ".log", + Version: version, + BuildTime: buildTime, + Filename: filename, + LoadTime: time.Now(), + LogLevel: "info", + LogFilename: filename + ".log", ShutdownGracePeriodMs: 10000, } err := json.LoadJsonFile(cfg.Filename, cfg) diff --git a/config/serverconfig.go b/config/serverconfig.go index dfcfeb3b2..69ee61ff0 100644 --- a/config/serverconfig.go +++ b/config/serverconfig.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -18,6 +18,7 @@ import ( const DEFAULT_MAX_PACKET_COUNT = 5000 const DEFAULT_IDLE_CONNECTION_TIMEOUT_MS = 300000 +const DEFAULT_MAX_UPLOAD_SIZE_BYTES = 26214400 type ServerConfig struct { AirgapEnabled bool `json:"airgapEnabled"` @@ -31,6 +32,7 @@ type ServerConfig struct { ClientParams ClientParameters `json:"client"` IdleConnectionTimeoutMs int `json:"idleConnectionTimeoutMs"` TimezoneScript string `json:"timezoneScript"` + MaxUploadSizeBytes int `json:"maxUploadSizeBytes"` } func (config *ServerConfig) Verify() error { @@ -56,5 +58,8 @@ func (config *ServerConfig) Verify() error { if len(config.TimezoneScript) == 0 { config.TimezoneScript = "/opt/sensoroni/scripts/timezones.sh" } + if config.MaxUploadSizeBytes == 0 { + config.MaxUploadSizeBytes = DEFAULT_MAX_UPLOAD_SIZE_BYTES + } return err } diff --git a/config/serverconfig_test.go b/config/serverconfig_test.go index 5d5163a86..edf83b87a 100644 --- a/config/serverconfig_test.go +++ b/config/serverconfig_test.go @@ -1,5 +1,5 @@ // Copyright 2019 Jason Ertel (jertel). All rights reserved. -// Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. +// Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. // // This program is distributed under the terms of version 2 of the // GNU General Public License. See LICENSE for further details. @@ -22,6 +22,7 @@ func TestVerifyServer(tester *testing.T) { if assert.Error(tester, err) { assert.Equal(tester, DEFAULT_MAX_PACKET_COUNT, cfg.MaxPacketCount) assert.Equal(tester, DEFAULT_IDLE_CONNECTION_TIMEOUT_MS, cfg.IdleConnectionTimeoutMs) + assert.Equal(tester, DEFAULT_MAX_UPLOAD_SIZE_BYTES, cfg.MaxUploadSizeBytes) assert.False(tester, cfg.DeveloperEnabled) } diff --git a/html/css/app.css b/html/css/app.css index 54b34a2e4..94713f000 100644 --- a/html/css/app.css +++ b/html/css/app.css @@ -1,6 +1,6 @@ /* Copyright 2019 Jason Ertel (jertel). All rights reserved. - Copyright 2020-2021 Security Onion Solutions, LLC. All rights reserved. + Copyright 2020-2022 Security Onion Solutions, LLC. All rights reserved. This program is distributed under the terms of version 2 of the GNU General Public License. See LICENSE for further details. @@ -62,6 +62,10 @@ margin-right: 6px; } +.contrast-bg { + background-color: var(--v-drawer_background-base) !important; +} + /* animations */ .waggle { animation-name: waggle; @@ -109,6 +113,12 @@ color: white !important; } +.show-all { + padding: 10px; + border-top: 2px dotted; + border-bottom: 2px dotted; +} + /* Sensoroni-specific element styles */ .hardwrap { word-wrap: break-word; @@ -164,6 +174,79 @@ a#title, a#title:visited, a#title:active, a#title:hover { white-space: nowrap; } +.case.summary { + overflow-x: hidden; + white-space: nowrap; + opacity: 60%; + font-size: 75%; +} + +.case.blocked { + display: block; + width: auto !important; + min-width: auto !important; +} + +.theme--light.v-application .case.tabular-row { + border-bottom: 1px dotted rgba(0,0,0,.05); +} + +.theme--dark.v-application .case.tabular-row { + border-bottom: 1px dotted hsla(0,0%,100%,.05); +} + +.case.tabular-label { + word-wrap: break-word; + font-weight: bold; +} + +.case.label { + display: inline-block; + width: 15%; + min-width: 6.0em; + padding-right: 10px; + text-align: right; + font-weight: bold; + word-wrap: break-word; +} + +.case.value { + text-align: left; + word-wrap: break-word; +} + +.case.raw { + overflow-x: hidden; +} + +.case.detail-field { + border-radius: .25em; + white-space: pre-wrap; + word-wrap: break-word; +} + +.no-underline { + text-decoration: none; +} + +.clicktoedit:hover { + cursor: pointer; + color: var(--v-primary-lighten1) !important; +} + +td.associated { + overflow-x: hidden; + text-overflow: ellipsis; +} + +tr.expandable:hover { + cursor: pointer; +} + +td.case { + white-space: normal !important; +} + td { white-space: nowrap; } @@ -189,4 +272,126 @@ td { #connection-indicator { color: white; -} \ No newline at end of file +} + +.comment-body { + width: 100%; +} + +.case-table table { + width: 100% !important; + table-layout: fixed; +} + +.markdown-body { + overflow: auto; + white-space: revert !important; + position: relative; +} + +.comment-body .markdown-body { + padding: 0.75em 1.25em; + border-radius: 0.25em; +} + +.comment-body .markdown-body pre, +.comment-body .markdown-body p { + margin: 8px 0; +} + +.markdown-body table { + border-collapse: collapse; + margin: 8px 0 1px 0; + text-align: left; +} + +.markdown-body td, +.markdown-body th { + padding: 8px; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin: 8px 0 8px 0; +} + +.theme--dark.v-application .markdown-body td, +.theme--dark.v-application .markdown-body th { + border: 1px solid hsla(0,0%,100%,.1); +} + +.theme--light.v-application .markdown-body td, +.theme--light.v-application .markdown-body th { + border: 1px solid rgba(0,0,0,.2); +} + +.theme--dark.v-application .markdown-body tr:nth-child(even) { + background-color: hsla(0,0%,100%,.05); +} + +.theme--light.v-application .markdown-body tr:nth-child(even) { + background-color: rgba(0,0,0,.05); +} + +.markdown-body th { + background-color: rgba(0,0,0,.1); +} + +/* motd background is darker, so some markdown table colors need to be different */ +.theme--dark.v-application #motd.markdown-body th { + background-color: rgba(0,0,0,.25); +} + +.theme--light.v-application #motd.markdown-body th { + background-color: rgba(0,0,0,.1); +} + + +.theme--dark.v-application .comment-body .markdown-body { + background-color: rgba(0,0,0,.2); +} + +.theme--light.v-application .comment-body .markdown-body { + background-color: rgba(0,0,0,.03); +} + +.markdown-body pre { + overflow-x: auto; + padding: 0.5em 0.7em !important; +} + +.v-application .markdown-body pre code { + display: block; + background-color: revert !important; + padding: 0 !important; +} + +.theme--light.v-application .markdown-body pre { + background-color: rgba(0,0,0,.05); + border-radius: 0.25em; +} + +.theme--dark.v-application .markdown-body pre { + background-color: hsla(0,0%,100%,.1); + border-radius: 0.25em; +} + +.case.avatar-font { + font-size: 11px; +} + +.case.markdown-icon{ + text-decoration: none; +} + +.case.markdown-icon i { + color: var(--v-secondary-lighten3); +} + +.case.markdown-icon:hover i { + color: var(--v-primary-base); +} diff --git a/html/index.html b/html/index.html index eaa1c0530..41160fa5e 100644 --- a/html/index.html +++ b/html/index.html @@ -1,7 +1,7 @@