Skip to content

Commit d22679d

Browse files
committed
Cb Defense fixes:
* Add new example script `list_devices.py` * Date/time stamps in the Device model object are now datetime objects not integers.
1 parent c126e8e commit d22679d

31 files changed

+158
-102
lines changed

bin/cbapi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import contextlib
55

66
from cbapi.six import iteritems
77
import sys
8-
from cbapi.utils import check_python_tls_compatibility
8+
from cbapi.connection import check_python_tls_compatibility
99

1010

1111
def check_tls(opts):

bin/cbapi-response

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ else:
1616

1717
from cbapi.response.rest_api import get_api_token
1818
from cbapi.six.moves.configparser import RawConfigParser
19-
from cbapi.utils import check_python_tls_compatibility
19+
from cbapi.connection import check_python_tls_compatibility
2020

2121

2222
@contextlib.contextmanager

docs/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ All products:
2121

2222
Cb Defense:
2323

24+
* Date/time stamps in the Device model object are now represented as proper Python datetime objects, rather than
25+
integers.
2426
* The ``policy_operations.py`` example script's "Replace Rule" command is fixed.
2527
* Add the Cb Live Response job-based API.
28+
* Add a new example script ``list_devices.py``
2629

2730
Cb Response:
2831

examples/defense/list_devices.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
from cbapi.defense.models import Device
5+
from cbapi.example_helpers import build_cli_parser, get_cb_defense_object
6+
7+
8+
def main():
9+
parser = build_cli_parser("List devices")
10+
device_options = parser.add_mutually_exclusive_group(required=False)
11+
device_options.add_argument("-i", "--id", type=int, help="Device ID of sensor")
12+
device_options.add_argument("-n", "--hostname", help="Hostname")
13+
14+
args = parser.parse_args()
15+
cb = get_cb_defense_object(args)
16+
17+
if args.id:
18+
devices = [cb.select(Device, args.id)]
19+
elif args.hostname:
20+
devices = list(cb.select(Device).where("hostNameExact:{0}".format(args.hostname)))
21+
else:
22+
devices = list(cb.select(Device))
23+
24+
print("{0:9} {1:40}{2:18}{3}".format("ID", "Hostname", "IP Address", "Last Checkin Time"))
25+
for device in devices:
26+
print("{0:9} {1:40}{2:18}{3}".format(device.deviceId, device.name, device.lastInternalIpAddress, device.lastContact))
27+
28+
29+
if __name__ == "__main__":
30+
sys.exit(main())
File renamed without changes.

examples/defense/policy_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def get_policy_by_name_or_id(cb, policy_id=None, name=None, return_all_if_none=F
4040

4141
def list_policies(cb, parser, args):
4242
for p in cb.select(Policy):
43-
print("Policy id {0}: {1} {2}".format(p.id, p.name, "({0})".format(p.description) if p.description else ""))
43+
print(u"Policy id {0}: {1} {2}".format(p.id, p.name, "({0})".format(p.description) if p.description else ""))
4444
print("Rules:")
4545
for r in p.rules.values():
4646
print(" {0}: {1} when {2} {3} is {4}".format(r.get('id'), r.get("action"),

src/cbapi/__init__.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import absolute_import
2-
import cbapi.six as six
2+
import cbapi.six
33

44
__title__ = 'cbapi'
55
__author__ = 'Carbon Black Developer Network'
@@ -8,13 +8,13 @@
88
__version__ = '1.3.5'
99

1010
# New API as of cbapi 0.9.0
11-
from .response.rest_api import CbEnterpriseResponseAPI, CbResponseAPI
12-
from .protection.rest_api import CbEnterpriseProtectionAPI, CbProtectionAPI
13-
from .defense.rest_api import CbDefenseAPI
11+
from cbapi.response.rest_api import CbEnterpriseResponseAPI, CbResponseAPI
12+
from cbapi.protection.rest_api import CbEnterpriseProtectionAPI, CbProtectionAPI
13+
from cbapi.defense.rest_api import CbDefenseAPI
1414

1515
# LEGACY APIs, will deprecated as of cbapi 2.0.0
1616
# only import these if the Python version is 2.x
17-
if six.PY2:
18-
from .legacy.cbapi import CbApi
19-
from .legacy import util
20-
from .legacy.bit9api import bit9Api
17+
if cbapi.six.PY2:
18+
from cbapi.legacy.cbapi import CbApi
19+
from cbapi.legacy import util
20+
from cbapi.legacy.bit9api import bit9Api

src/cbapi/connection.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,30 @@
3232

3333
from .cache.lru import lru_cache_function
3434
from .models import CreatableModelMixin
35+
from .utils import calculate_elapsed_time
3536

3637

3738
log = logging.getLogger(__name__)
3839

3940

40-
def calculate_elapsed_time_new(td):
41-
return td.total_seconds()
42-
43-
44-
def calculate_elapsed_time_old(td):
45-
return float((td.microseconds +
46-
(td.seconds + td.days * 24 * 3600) * 10**6)) / 10**6
41+
def check_python_tls_compatibility():
42+
try:
43+
tls_adapter = CbAPISessionAdapter(force_tls_1_2=True)
44+
except Exception as e:
45+
ret = "TLSv1.1"
4746

47+
if "OP_NO_TLSv1_1" not in ssl.__dict__:
48+
ret = "TLSv1.0"
49+
elif "OP_NO_TLSv1" not in ssl.__dict__:
50+
ret = "SSLv3"
51+
elif "OP_NO_SSLv3" not in ssl.__dict__:
52+
ret = "SSLv2"
53+
else:
54+
ret = "Unknown"
55+
else:
56+
ret = "TLSv1.2"
4857

49-
if sys.version_info < (2, 7):
50-
calculate_elapsed_time = calculate_elapsed_time_old
51-
else:
52-
calculate_elapsed_time = calculate_elapsed_time_new
58+
return ret
5359

5460

5561
class CbAPISessionAdapter(HTTPAdapter):

src/cbapi/defense/models/deviceInfo.yaml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ properties:
4141
type: string
4242
createTime:
4343
type: integer
44-
format: int64
44+
format: epoch-ms-date-time
4545
policyId:
4646
type: integer
4747
format: int64
@@ -59,26 +59,26 @@ properties:
5959
- "MISSION_CRITICAL"
6060
lastVirusActivityTime:
6161
type: integer
62-
format: int64
62+
format: epoch-ms-date-time
6363
firstVirusActivityTime:
6464
type: integer
65-
format: int64
65+
format: epoch-ms-date-time
6666
activationCodeExpiryTime:
6767
type: integer
68-
format: int64
68+
format: epoch-ms-date-time
6969
organizationName:
7070
type: string
7171
sensorVersion:
7272
type: string
7373
registeredTime:
7474
type: integer
75-
format: int64
75+
format: epoch-ms-date-time
7676
lastContact:
7777
type: integer
78-
format: int64
78+
format: epoch-ms-date-time
7979
lastReportedTime:
8080
type: integer
81-
format: int64
81+
format: epoch-ms-date-time
8282
windowsPlatform:
8383
type: string
8484
x-nullable: true
@@ -115,7 +115,7 @@ properties:
115115
- "UNINSTALLED_SERVER"
116116
deregisteredTime:
117117
type: integer
118-
format: int64
118+
format: epoch-ms-date-time
119119
sensorStates:
120120
type: array
121121
items:
@@ -146,12 +146,12 @@ properties:
146146
type: string
147147
time:
148148
type: integer
149-
format: int64
149+
format: epoch-ms-date-time
150150
rootedBySensor:
151151
type: boolean
152152
rootedBySensorTime:
153153
type: integer
154-
format: int64
154+
format: epoch-ms-date-time
155155
lastInternalIpAddress:
156156
type: string
157157
lastExternalIpAddress:
@@ -171,37 +171,37 @@ properties:
171171
type: boolean
172172
lastResetTime:
173173
type: integer
174-
format: int64
174+
format: epoch-ms-date-time
175175
lastShutdownTime:
176176
type: integer
177-
format: int64
177+
format: epoch-ms-date-time
178178
scanStatus:
179179
type: string
180180
scanLastActionTime:
181181
type: integer
182-
format: int64
182+
format: epoch-ms-date-time
183183
scanLastCompleteTime:
184184
type: integer
185-
format: int64
185+
format: epoch-ms-date-time
186186
linuxKernelVersion:
187187
type: string
188188
avEngine:
189189
type: string
190190
avLastScanTime:
191191
type: integer
192-
format: int64
192+
format: epoch-ms-date-time
193193
rootedByAnalytics:
194194
type: boolean
195195
rootedByAnalyticsTime:
196196
type: integer
197-
format: int64
197+
format: epoch-ms-date-time
198198
testId:
199199
type: integer
200200
avMaster:
201201
type: boolean
202202
uninstalledTime:
203203
type: integer
204-
format: int64
204+
format: epoch-ms-date-time
205205
name:
206206
type: string
207207
status:

src/cbapi/models.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
from .errors import ServerError, InvalidObjectError
1818
from .oldmodels import BaseModel
1919
import logging
20+
from datetime import datetime
21+
from cbapi.utils import calculate_elapsed_time
22+
2023
log = logging.getLogger(__name__)
2124

2225

@@ -62,8 +65,10 @@ def __new__(mcs, name, bases, clsdict):
6265

6366
if field_format.startswith('int'):
6467
setattr(cls, field_name, FieldDescriptor(field_name, coerce_to=int))
65-
elif field_format == "date-time":
66-
setattr(cls, field_name, DateTimeFieldDescriptor(field_name))
68+
elif field_format == "iso-date-time":
69+
setattr(cls, field_name, IsoDateTimeFieldDescriptor(field_name))
70+
elif field_format == "epoch-ms-date-time":
71+
setattr(cls, field_name, EpochDateTimeFieldDescriptor(field_name, 1000.0))
6772
elif field_format == "boolean":
6873
setattr(cls, field_name, FieldDescriptor(field_name, coerce_to=bool))
6974
elif field_format == "array":
@@ -119,17 +124,37 @@ def __get__(self, instance, instance_type=None):
119124
return ret or {}
120125

121126

122-
class DateTimeFieldDescriptor(FieldDescriptor):
127+
class IsoDateTimeFieldDescriptor(FieldDescriptor):
123128
def __init__(self, field_name):
124-
super(DateTimeFieldDescriptor, self).__init__(field_name)
129+
super(IsoDateTimeFieldDescriptor, self).__init__(field_name)
125130

126131
def __get__(self, instance, instance_type=None):
127-
d = super(DateTimeFieldDescriptor, self).__get__(instance, instance_type)
132+
d = super(IsoDateTimeFieldDescriptor, self).__get__(instance, instance_type)
128133
return convert_from_cb(d)
129134

130135
def __set__(self, instance, value):
131136
parsed_date = convert_to_cb(value)
132-
super(DateTimeFieldDescriptor, self).__set__(instance, parsed_date)
137+
super(IsoDateTimeFieldDescriptor, self).__set__(instance, parsed_date)
138+
139+
140+
class EpochDateTimeFieldDescriptor(FieldDescriptor):
141+
epoch = datetime.utcfromtimestamp(0)
142+
143+
def __init__(self, field_name, multiplier=1.0):
144+
super(EpochDateTimeFieldDescriptor, self).__init__(field_name)
145+
self.multiplier = float(multiplier)
146+
147+
def __get__(self, instance, instance_type=None):
148+
d = super(EpochDateTimeFieldDescriptor, self).__get__(instance, instance_type)
149+
epoch_seconds = d / self.multiplier
150+
return datetime.utcfromtimestamp(epoch_seconds)
151+
152+
def __set__(self, instance, value):
153+
if isinstance(value, datetime):
154+
new_value = calculate_elapsed_time(value - self.epoch) * self.multiplier
155+
else:
156+
new_value = value
157+
super(EpochDateTimeFieldDescriptor, self).__set__(instance, new_value)
133158

134159

135160
class ForeignKeyFieldDescriptor(FieldDescriptor):

0 commit comments

Comments
 (0)