Read user guide here.
When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment include:
Using welcoming and inclusive language Being respectful of differing viewpoints and experiences Gracefully accepting constructive criticism Focusing on what is best for the community Showing empathy towards other community members Examples of unacceptable behavior by participants include:
The use of sexualized language or imagery and unwelcome sexual attention or advances, trolling, insulting/derogatory comments, and personal or political attacks, Public or private harassment, Publishing others' private information, such as a physical or electronic address, without explicit permission or other conduct which could reasonably be considered inappropriate in a professional setting
Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [email protected]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
Following is the official pull request template:
Status
**READY/IN DEVELOPMENT/HOLD**
Description
A few sentences describing the overall goals of the pull request's commits.
Related PRs
List related PRs against other branches:
branch | PR
------ | ------
other_pr_production | [link]()
other_pr_master | [link]()
Todos
- [ ] Tests
- [ ] Documentation
Deploy Notes
Notes regarding deployment the contained body of work. These should note any
db migrations, etc.
Steps to Test or Reproduce
Outline the steps to test or reproduce the PR here.
git pull --prune
git checkout <feature-branch>
## Impacted Areas in Application
List general components of the application that this PR will affect:
SecureTea Tool Features | Progress |
---|---|
Securetea Dashboard | Yes |
Notify by Twitter | Yes |
Notify by Telegram | Yes |
Notify by Slack | Yes |
Notify by Twilio SMS | Yes |
Notify by Amazon (AWS-SES) | Yes |
Notify by WhatsApp | Yes |
Insecure Header Detection | Yes |
SecureTea Firewall | Yes |
SecureTea IDS/IPS | Yes |
SecureTea System Log Monitor | Yes |
SecureTea Server Log Monitor | Yes |
SecureTea Anti-Virus | Yes |
SecureTea Malware Analysis | Yes |
SecureTea Auto Server Patcher | Yes |
Web Defacement Detection | Yes |
SecureTea OSINT | Yes |
IoT Anonymity checker | Yes |
GUI Last login | Yes |
Detection of malcious device | Yes |
This image contains VSCode server to develop directly within the container
$ docker pull ukjpco/securetea:dev
$ docker run -d --name securetea -P ukjpco/securetea:dev
$ git pull https://github.com/OWASP/SecureTea-Project/ securetea
$ cd securetea
$ docker build . --tag securetea:latest
$ docker run -d --name securetea -P securetea:latest
Run $ docker ps
to see which port the container is exposing for development
its recommended to have a firewall rule blocking external or unauthorized access to
this.
Before continuing go through the Before installation.
- Install virtualenv
pip install virtualenv
- Create a virtual environment named
venv1
virtualenv venv1
- Activate virtual environment
venv1
source venv1/bin/activate
- Fork this project, by visiting SecureTea-Project
- Clone the forked repository using
git clone <your-fork-url>
- Navigate to the project folder using
cd SecureTea-Project
- Install the dependencies using
pip install -r requirements.txt
If you want to work on Angular JS, the web GUI is the portion for you..The Web UI requires ServerApp running. To run ServerApp,
- Navigate to
/ServerApp
by using commandcd ServerApp
python3 run.py
to start ServerApp- ServerApp runs on
http://localhost:5000
. This is the END-POINT for the GUI - To check if ServerApp is running, run
http://localhost:5000
in a Web Browser. A white/blank screen indicates proper working of ServerApp
The preffered web browser to use Web UI and test ServerApp is Google Chrome.
ng serve doesn't have to be sudo, i.e, it doesn't require root privileges. ServerApp needs to be run as sudo, i.e., you require root privilege to run it.
Follow the following steps to setup Web UI
cd gui
ng serve
- Visit http://localhost:4200 to view your project, END-POINT is http://localhost:5000.
- The EndPoint Secret is
PASSWD
by default.
Registering for Web UI
- Enter Name
- Enter Password
- ServerApp should be running. END-POINT is URL where ServerApp runs. By default this is http://localhost:5000`
- EndPoint Secret is
PASSWD
by default
You can now login and edit the files and further develop the Web Gui.
Using @takeInput
you can easily setup interactive mode for any of the features you add.
In order to write a function compatible with @takeInput
, have a look at the following example:
@takeInput
def configureFeaturename(self):
default = load_default('featurename') # Load the default configuration from securetea.conf
return {
'input': {
'input_field_name': 'messge to print',
'input_filed_name2': 'message to print'
}
'default': default
}
@takeInput
demands a nested dictionary following the above style in return value of the function.
Thats it! You don't need to worry more, interactive mode is setup now. Make sure to update the logic flow in args_helper.py
.
Step 1 - Add your input fields to /securetea/args/config.py
"module_name": {
"input_field_name1": "XXXX",
"input_field_name2": "XXXX"
},
Step 2 - If the module is to be accessible like python3 SecureTea.py --module_name add the following to /securetea/args/arguments.py
parser.add_argument(
'--module_name',
required=False,
action='store_true',
help='Help string for Module'
)
Step 3 - Make the following changes to securetea/args/args_helper.py
self.module_name_provided = False
# near line 190
@takeInput
def configuremodule_name(self):
"""
Returns the format to configure module_name
"""
self.logger.log('module_name configuration setup')
default = load_default('module_name')
return {
'input': {
"input_field_name1": "String to be displayed before taking input_field_name1",
"input_field_name2": "String to be displayed before taking input_field_name1"
},
'default': default
}
# Start module_name configuration setup
module_name = self.configuremodule_name()
if module_name:
self.cred['module_name'] = module_name
self.module_name_provided = True
if self.args.module_name and not self.module_name_provided:
module_name = self.configuremodule_name()
if module_name:
self.cred['module_name'] = module_name
self.module_name_provided = True
# near line 1250
self.malware_analysis_provided or
# add this in the if statement
# At end of class ArgsHelper near line 1280
'module_name': self.mdule_name_provided,
# add this in the return statement
Step 4 - To finally integrate all the changes change securetea/core.py
# near line 100
self.malware_analysis_provided = args_dict['malware_analysis']
try:
if self.cred['module_name']:
self.module_name_provided = True
self.cred_provided = True
except KeyError:
self.logger.log(
"module_name configuration parameter not set.",
logtype="error"
)
if self.module_name_provided:
# run the actual module working here.
To add logging feature to your module, have a look at the following example:
>> from securetea import Logger # import logger
>> debug = False # or True, as per your need
>> logger_obj = logger.SecureTeaLogger(
__name__,
debug
)
>> logger_obj.log(
"String formatted message",
logtype="error" # by default logger level is set to info
)
Currently, there are three levels:
- info (default)
logtype="info"
- error
logtype="error"
- warning
logtype="warning"
To add a new rule compatible with the current architecture look at the following code snippet:
def rule_name(scapy_packet):
# Peform logic by disecting the scapy_packet
return {
'action': 0 # action specified if the rule matches, 0: BLOCK, 1: ACCEPT
'result': 0 # if rule matches, then 1 else 0
}
Add this function to packet_filter.PacketFilter
.
xnor
decorator takes in all the effort to decide whether to allow or drop the packet based on the rule and user configuration. You don't need to worry about writing logic flow for each and every rule, xnor
is highly efficient and uses the following XNOR table to decide whether to allow or drop the packet.
Action | Result | Final |
---|---|---|
0 | 0 | 1 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
where,
- 1: Allow
- 0: Block
To use the @xnor
decorator, follow the following format:
>> from securetea.lib.firewall import utils
>> @utils.xnor()
def rule_name():
# rule logic
# return according to the format
return {
'action': 0,
'result': 0
}
To add a new rule for an atack vector to IDS, classify the attack vector into the following two categories:
- Reconnaissance (peformed for information gathering)
- Remaining attack vectors (R2L attack vectors like DoS, MiTM attacks)
Currently, the IDS supports the detection of the followings:
- General scans: TCP ACK & TCP Window, UDP, ICMP scans
- Stealth scans: FIN, XMAS, NULL scans
- OS fingerprinting scans
Adding a new rule is pretty simple, follow the following function format and add this to the DetectRecon
class in recon_attack.py
.
def rule_name(self, pkt=None):
"""
pkt (scapy packet): Packet to dissect and observe
"""
if pkt is not None:
# perform your logic here
# log any abnormalities
After this addition, please make sure to update run
method in DetectRecon
class in recon_attack.py
. This will enable the IDS engine to detect the new rule.
Currently, the IDS supports the detection of the following attack vectors:
- DoS attacks
- CAM Table Exhaustion
- DHCP Exhaustion
- Man in The Middle (MiTM) / ARP cache poisoning
- SYN flood attack
- Ping of death
- Land attack
- BGP Abuse
- DNS Amplification
- Wireless
- Deauthentication attack
- Hidden node attack
- SSID spoofing
- Fake access point
Adding a new rule is again pretty simple, except here the rules need to be as an independent object oriented module. The rules should be encapsulated within an independent class.
Example:
class RuleName(object):
def __init__(self, debug=False):
# Initialize your variables
def detect_attack(self, pkt=None):
"""
pkt (scapy_object): Packet to dissect and observe
"""
# perform attack detection logic here
# log any abnormalities
This independent module should be categorized either as Wired attack or Wireless attack, and should be placed in the respective directory. The next step is to update the R2LEngine
class in lib/ids/r2l_rules/r2l_engine.py
. This involves the following, creating an object of your rule class in R2LEngine
constructor, and then updating the run
method in R2LEngine
class. That's it! IDS engine will pick up the new rules and pass the network packets.
Currently, system log monitor supports the following:
/etc/passwd
/etc/shadow
/var/log/auth.log
/var/log/faillog
/var/log/syslog
- Detect backdoors by detecting user sharing the same numerical ID
- Detect user existing without a password that may lead to privilege escalation
- Detect if there is any sync between
/etc/passwd
&/etc/shadow
else system has been manipulated - Detect non-standard hashing algorithm used in passwords to guess system manipulation
- Detect login attempts
- Detect password brute-force
- Detect harmful commands executed as sudo
- Detect port scans by observing quick “Received Disconnect”
- Detect SSH login attempts & brute-force
- Detect malicious sniffer by extracting PROMISC mode
Extending support for various Linux distribution is pretty simple, Linux uses the same format for log storing, hence log parsing remains the same for various Linux flavours, the only change that happens is the path of the log file.
To add path for a log file for a different Linux distro, update the mapping in the modules.
An example would be:
# OS name to password-log path map
self.system_log_map = {
"debian": "/etc/passwd",
"distro_name": "/path_to_log_file"
}
After that, make sure to update the categorize_os()
function in log_monitor/system_log/utils.py
.
An example would be:
os_name = get_system_name()
if os_name in ["ubuntu", "kali", "backtrack", "debian"]:
return "debian"
# elif some other OS, add their name
elif os_name in ["new_os"]:
return "distro_name"
else: # if OS not in list
return None
System log aggregator to disparate server log files, organize the useful data and apply intelligence to detect intrusion activities.
- Extending support for more server log files (writing a parser)
- Extending server log support for more distributions
- Adding new log rules
Currently, the server log monitor supports the following log file types:
- Apache
- Nginx
The following suspicious activities/attacks can be detected:
-
Attacks
-
Denial of Service (DoS) attacks
-
Cross site scripting (XSS) injection
-
SQL injection (SQLi)
-
Server Side Request Forgery (SSRF)
-
Local file inclusion (LFI)
-
Web shell injection
-
Reconnaissance attacks
-
Web crawlers / spiders / bots
-
URL Fuzzing
-
Port scans
-
Bad user agents
-
Log bad/suspicious IP (later on picked up by Firewall to block incoming request from that IP)
-
-
User defined rules:
- Filter based on selected IPs
- Filter based on response code
Adding a new parser is pretty easy and straight-forward to begin with. The basic outline of the parser goes like this.
class ParserName(object):
def __init__(self, debug=False, path=None, window=30):
# define all the variables here
# take inspiration from the current parser
def parse(self):
# use regex or any other option of your choice
# to parse the log file
def update_dict(self, ip, ep_time, get, status_code, user_agent):
# update the dict with the parsed (formatted data)
Add this parser to the parser
directory. Keeping this in mind and taking inspiration from the current parsers is the best way to extend support for other server log files. Apart from this, please be sure to add you parser details to engine.py
, after that it'll be automatically picked up for rest of the tasks.
Extending support for more distributions is easy, one needs to just add the corresponding log file path to the mapping_dict
in engine.py
, it goes like this.
# OS to log file path mapping
self.system_log_file_map = {
"apache": {
"debian": "/var/log/apache2/access.log",
"fedora": "/var/log/httpd/access_log",
"freebsd": "/var/log/httpd-access.log"
},
"nginx": {
"debian": "/var/log/nginx/access.log"
}
}
Update the above mapping dict with the OS of your choice.
There are two types of rules, regex rules and payload rules. Both of them are stored in .txt
format in the rules
directory. To extend the rules, one just needs to add new rules (both regex and payload) to the corresponding file, which can be easily done using a text editor. It's that simple to add a rule, the processing engine will load all the rules automatically.
SecureTea real-time signature & heuristic based antivirus.
The following features are currently supported:
-
Auto fetch updates: Smart update mechanism, that keeps track of the last update and resumes update from the last downloaded file. User can configure to switch off and switch on the auto-update feature.
-
Real-Time monitoring: Scan as soon as a file is modified or a new file is added.
-
Scanner engine: Scanner engine runs on 3 process, they are as follows:
- Hash Signature scanner
- Yara Heuristic scanner
- Clam AV Scanner
-
YARA rules can detect:
- Viruses
- Worms
- Ransomware
- Adware
- Spyware
- Rootkits
- RATs
-
Leveraging the power of VirusTotal API: Optional for users, provides an easy option for them to test for specific files against multiple anti-viruses & in a safe sandbox environment, i.e. after a file is detected malicious, the file will be put under VirusTotal test for a final confirmation.
-
Monitor orphaned files: Use SUID, SGID and read capabilities in Linux to separate orphaned files and check if any file is granted more capabilities than it should be.
-
Keeps an eye on USB devices: Start scanning the USB device as soon as it is plugged in & report for any virus/malware found.
-
Cleaning the found files: Opt for either auto-delete or manual delete option, in auto-delete the file found malicious is automatically deleted, whereas in manual it requires the confirmation of the user.
-
Custom and Full scan options
To integrate a new scanner project into ours follow along. You need to worry about multi-threading and multi-processing, these will be handled by the Scanner
parent (base) class. To do this, create a new scanner file and inherit the Scanner
class. An example would be:
from securetea.lib.antivirus.scanner.scanner_parent import Scanner
class NewScanner(Scanner):
def __init__(self, debug=False, config_path=None, file_list=None, vt_api_key=None):
# Initialize parent class
super().__init__(debug, config_path, file_list, vt_api_key)
# Perform rest of your operations
def scan_file(self, file_path):
# Peform the logic to scan the file
# and log the results
To get a more clear understanding of this, take inspiration from rest of the scanner engines.
To extend support for more OS, add the OS category name to the following config.json
in config directory and the required variables to it, rest will be automatically picked by the engine.
{
"debian": {
"update": {
"hash": {
"storage": "/etc/securetea/antivirus/md5_hash/"
},
"yara": {
"storage": "/etc/securetea/antivirus/yara/"
}
},
"scanner": {
"malicious_file_log_path": "/etc/securetea/antivirus/malicious_files.log",
"hash": {
"threads": 2
},
"yara": {
"threads": 2
},
"clamav": {
"threads": 2
}
},
"monitor": {
"threshold_min": 20,
"password_log_file": "/etc/passwd"
}
}
}
SecureTea Auto Server Patcher will patch the server configurations for highest security & help overcome common security deployment mistakes.
The following features are currently supported:
-
Auto update packages
-
Set password expiration & password strength rules
-
Check for rootkits
-
Auto remove discarded package
-
Enhance IP TABLE rules:
- Force SYN packets check
- Drop XMAS packets
- Drop null packets
- Drop incoming packets with fragments
-
Configure
/etc/sysctl.conf
- Disable IP forwarding & IP source routing
- Disable sent packets redirects
- Disable ICMP redirect acceptance
- Enable IP spoofing protection
- Enable bad error message protection
-
Patch Apache server configurations
- Prevent server from broadcasting version number
- Turn off TRACE method to prevent Cross-Site Scripting
- X-powered by headers
-
Configure SSH
- Disallow root access via SSH
- Disallow SSH from trusting a host based only on its IP
- Prevent users from logging into SSH with an empty password
- Sop the possibility of the server sending commands back to the client
- Drop the SSH connection after 5 failed authorization attempts
- Disable weak ciphers
- Disables password authentication and defers authorization to the key-based PAM
- Log out idle users after 15 minutes
- Configure server checks whether the session is active before dropping
-
List all the possible SSL vulnerabilities in the server using SSL Labs API
- Beast attack
- Poodle
- Poodle TLS
- RC4
- Heartbeat
- Heartbleed
- Ticketbleed
- OpenSSL CCS
- OpenSSL padding
- Robot attack
- Freak
- Logjam
- Drown attack
The current list of commands can be extracted from the commands.json
file. The current version is the following:
{
"debian": {
"commands": [
"iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP",
"iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP",
"iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP",
"iptables -A INPUT -f -j DROP",
"apt-get update",
"apt-get autoremove",
"apt-get remove telnet",
"chkrootkit"
]
}
}
To add a new command, just add the command to the list of commands for the respected OS. Also, to extend support for other OS, add the list of commands for that OS under the respected OS key.
The following is the current patcher configuration. To modify a command and it's new value edit the following configuration file. Same can be done to extend support for more OS.
{
"debian": {
"/etc/sysctl.conf": {
"sep": "=",
"config": {
"net.ipv4.ip_forward": "0",
"net.ipv4.conf.all.send_redirects": "0",
"net.ipv4.conf.default.send_redirects": "0",
"net.ipv4.conf.all.accept_redirects": "0",
"net.ipv4.conf.default.accept_redirects": "0",
"net.ipv4.icmp_ignore_bogus_error_responses": "1"
}
},
"/etc/apache2/apache2.conf": {
"sep": " ",
"config": {
"ServerTokens": "Prod",
"ServerSignature": "Off",
"Header always unset X-Powered-By": " ",
"TraceEnable": "Off"
}
},
"/etc/ssh/ssh_config": {
"sep": " ",
"config": {
"PermitRootLogin": "no",
"IgnoreRhosts": "yes",
"HostbasedAuthentication": "no",
"PermitEmptyPasswords": "no",
"X11Forwarding": "no",
"MaxAuthTries": "5",
"Ciphers": "aes128-ctr,aes192-ctr,aes256-ctr",
"UsePAM": "yes",
"ClientAliveInterval": "900",
"ClientAliveCountMax": "0"
}
},
"/etc/login.defs": {
"sep": " ",
"config": {
"PASS_MAX_DAYS": "30",
"PASS_MIN_DAYS": "0",
"PASS_WARN_AGE": "7"
}
}
}
}
Uses bash to log history
Features:
-
Log commands run across terminals
-
At a terminal level. Hence indepenedent of method of login
-
Uses inbuilt system logger service : rsyslog
-
Writes logs at /var/log/securetea_history_logger.log
SecureTea History Logger would detect vommands executed on the system along with the ip address and mac address. This helps detecting commands run my malicious users so they can be reverted.
Monitor server files to detect any changes, roll back to default in case of defacement.
Features:
-
Auto locate the server files based on the user choice of server (i.e. Apache, Nginx, etc.) and the operating system detected.
-
Allow user to overwrite the above default auto-located file path and use their custom file path.
-
Scan the directory for files and generate a cache / backup of the files.
-
Generate SHA 256 hashes of each file and use them for comparison.
-
Scan source code of each web page and find if the web page is defaced based on Attack Signatures found on previoulsty defaced website.
-
Scan the webpage by using Natural Language Processing and Machine Learning, and predict if the webpage is defaced.
SecureTea Web Defacement Detection would detect file addition, deletion and modification and roll back to the original file immediately. It would not allow addition of any new files, deletion of files or any type of modification to the current existing files. It would also tell what content was modified. Additional Features such as Attack Signature Based Detection and Machine Learning detection model, help to detect defacement on dynamic websites. The attack signatures are extracted from defaced web pages and then caompared with server's webpages to detect defacement. We use a hybrid website defacement detection model that is based on machine learning techniques and attack signatures. The machine leaning-based component is able to detect defaced web pages with a high level of accuracy and the detection profile can be learned using a dataset of both normal pages and defaced pages. The signature-based component helps boost the processing speed for common forms of defaced attacks.
ml_Deface.mp4
The following servers and the OS are supported, to add new servers and OS, extend the below configuration accordingly.
{
"debian": {
"apache": "/var/www/html",
"nginx": "/usr/share/nginx/html"
}
}
SecureTea Server mode includes the running of the following modules:
- Firewall
- Server Log Monitor
- AntiVirus
- System Log Monitor
- Intrusion Detection System (IDS)
- Auto Server Patcher
- Web Deface Detection
This mode is designed for an easy option to set up complete server protection using a single argument i.e. --server-mode
. It will ask whether to load the previously saved configurations or enter a new one. Choosing to go with a new configuration will start an interactive setup of the respected modules. Also, user can skip any module among the above 7 modules.
SecureTea System mode includes the running of the following modules:
- Firewall
- AntiVirus
- System Log Monitor
- Intrusion Detection System (IDS)
This mode is designed for an easy option to set up complete system (PC / laptop) protection using a single argument i.e. --system-mode
. It will ask whether to load the previously saved configurations or enter a new one. Choosing to go with a new configuration will start an interactive setup of the respected modules. Also, user can skip any module among the above 4 modules.
SecureTea IoT mode includes the running of the following modules:
- Firewall
- Intrusion Detection System (IDS)
- IoT Anonymity Checker
This mode is designed for an easy option to set up complete IoT device protection using a single argument i.e. --iot-mode
. It will ask whether to load the previously saved configurations or enter a new one. Choosing to go with a new configuration will start an interactive setup of the respected modules. Also, user can skip any module among the above 3 modules.
To run tests using unittest
follow the following steps:
- Navigate to parent directory, i.e.
SecureTea-Project
directory. - Run the following command:
python -m unittest
To run tests using pytest
follow the following steps:
- Navigate to the parent directory, i.e.
SecureTea-Project
directory. - Run the following command:
pytest
Currently, SecureTea-Project uses sqlite3 database.
MIT License
Copyright (c) 2019 OWASP SecureTea-Project Team - http://owasp.org
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.