Skip to content

Commit 60d5712

Browse files
authored
Merge pull request #50 from OpenLXP/code-sync
Latest sync with P1 master branch
2 parents 8e2e8ca + d14d28f commit 60d5712

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+7288
-4588
lines changed

.github/workflows/cd-workflow.yml

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -47,46 +47,3 @@ jobs:
4747
echo "Docker network successfully created"
4848
echo "Running coverage unit test"
4949
docker compose --env-file ./.env run app sh -c "python manage.py waitdb && coverage run manage.py test --tag=unit && flake8 && coverage report && coverage report --fail-under=80"
50-
51-
# sonarcloud:
52-
# name: SonarCloud
53-
# runs-on: ubuntu-latest
54-
# steps:
55-
# - uses: actions/checkout@v2
56-
# with:
57-
# fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
58-
# - name: SonarCloud Scan
59-
# uses: SonarSource/sonarcloud-github-action@master
60-
# env:
61-
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
62-
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
63-
64-
build:
65-
# require dependency from step above
66-
needs: code-test
67-
name: Build Docker Image
68-
runs-on: ubuntu-latest
69-
steps:
70-
- name: Checkout Code
71-
uses: actions/checkout@v2
72-
- name: Configure AWS credentials
73-
uses: aws-actions/configure-aws-credentials@v1
74-
with:
75-
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
76-
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
77-
aws-region: ${{ secrets.AWS_REGION }}
78-
- name: Login to Amazon ECR
79-
id: login-ecr
80-
uses: aws-actions/amazon-ecr-login@v1
81-
with:
82-
mask-password: 'true'
83-
- name: Build, tag, and push image to Amazon ECR
84-
env:
85-
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
86-
ECR_REPOSITORY: ${{ secrets.ECR_REPO }}
87-
IMAGE_TAG: latest
88-
run: |
89-
echo "Starting docker build"
90-
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
91-
echo "Pushing image to ECR..."
92-
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ git clone https://github.com/OpenLXP/openlxp-xss.git
4343
| AWS_ACCESS_KEY_ID | The Access Key ID for AWS |
4444
| AWS_SECRET_ACCESS_KEY | The Secret Access Key for AWS |
4545
| AWS_DEFAULT_REGION | The region for AWS |
46+
| CORS_ALLOWED_ORIGINS | List of trusted origins that are allowed to make cross-origin requests to the server |
4647
| DB_HOST | The host name, IP, or docker container name of the database |
4748
| DB_NAME | The name to give the database |
4849
| DB_PASSWORD | The password for the user to access the database |

app/core/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class TermAdmin(admin.ModelAdmin):
9090
'modified', )
9191
fieldsets = (
9292
(None, {'fields': ('iri', 'name', 'uuid', 'description', 'status',)}),
93-
('Info', {'fields': ('data_type', 'use',
93+
('Info', {'fields': ('data_type', 'use', 'learning_type',
9494
'multiple_expected',
9595
'source',)}),
9696
('Connections', {'fields': ('term_set', 'mapping',)}),

app/core/models.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
r'| \xF1-\xF3{3} # planes 4-15 '
3535
r'| \xF4\x80-\x8F{2} # plane 16 )*\Z))')
3636

37+
source_tag = 'ldss:'
38+
graph_tag = '@graph'
39+
context_tag = '@context'
40+
3741

3842
def validate_version(value):
3943
check = re.fullmatch('[0-9]*[.][0-9]*[.][0-9]*', value)
@@ -79,33 +83,33 @@ def json_ld(self):
7983
graph = {}
8084
context = {}
8185
# add elements to graph and context
82-
graph['@id'] = 'ldss:' + self.iri
86+
graph['@id'] = source_tag + self.iri
8387
graph['@type'] = 'rdfs:Class'
8488
graph['rdfs:label'] = self.name
8589
context['rdfs'] = 'http://www.w3.org/2000/01/rdf-schema#'
8690
if hasattr(self, 'childtermset'):
8791
graph['schema:domainIncludes'] = {
88-
'@id': 'ldss:' +
92+
'@id': source_tag +
8993
self.childtermset.parent_term_set.iri}
9094
context['schema'] = 'https://schema.org/'
9195
# iterate over child term sets and collect their graphs and contexts
9296
children = []
9397
for kid in self.children.filter(status='published'):
9498
kid_ld = kid.json_ld()
95-
children.extend(kid_ld['@graph'])
99+
children.extend(kid_ld[graph_tag])
96100
# add children's context to current context, but current has
97101
# higher priority
98-
context = {**kid_ld['@context'], **context}
102+
context = {**kid_ld[context_tag], **context}
99103
# iterate over terms and collect their graphs and contexts
100104
terms = []
101105
for term in self.terms.filter(status='published'):
102106
term_ld = term.json_ld()
103-
terms.extend(term_ld['@graph'])
107+
terms.extend(term_ld[graph_tag])
104108
# add terms' context to current context, but current has higher
105109
# priority
106-
context = {**term_ld['@context'], **context}
110+
context = {**term_ld[context_tag], **context}
107111
# return the graph and context
108-
return {'@context': context, '@graph': [graph, *children, *terms]}
112+
return {context_tag: context, graph_tag: [graph, *children, *terms]}
109113

110114
def mapped_to(self, target_root):
111115
"""Return dict of Terms mapped to anything in target_root string"""
@@ -149,13 +153,18 @@ class Term(TimeStampedModel):
149153
('Optional', 'Optional'),
150154
('Recommended', 'Recommended'),
151155
]
156+
TYPE_CHOICES = [('Learning Resource', 'Learning Resource'),
157+
('Learning Event', 'Learning Event'),
158+
('Both', 'Both'),
159+
]
152160
name = models.SlugField(max_length=255, allow_unicode=True)
153161
description = models.TextField(null=True, blank=True)
154162
iri = models.SlugField(max_length=255, unique=True,
155163
allow_unicode=True, primary_key=True)
156164
uuid = models.UUIDField(default=uuid4, editable=False, unique=True)
157165
data_type = models.CharField(max_length=255, null=True, blank=True)
158166
use = models.CharField(max_length=255, choices=USE_CHOICES)
167+
# multiple_expected fields added in migration 0008
159168
multiple_expected = models.BooleanField(default=True,
160169
help_text="Whether multiple"
161170
" values "
@@ -203,7 +212,7 @@ def json_ld(self):
203212
graph = {}
204213
context = {}
205214
# add elements to graph and context
206-
graph['@id'] = 'ldss:' + self.iri
215+
graph['@id'] = source_tag + self.iri
207216
graph['@type'] = 'rdf:Property'
208217
if self.description is not None and len(self.description.strip()) > 0:
209218
graph['rdfs:comment'] = self.description
@@ -213,15 +222,15 @@ def json_ld(self):
213222
'@id': data_type_matching[self.data_type]}
214223
if self.mapping.exists():
215224
graph['owl:equivalentProperty'] = [
216-
{'@id': 'ldss:' + alt.iri} for alt in self.mapping.all()]
225+
{'@id': source_tag + alt.iri} for alt in self.mapping.all()]
217226
context['owl'] = 'http://www.w3.org/2002/07/owl#'
218227
graph['rdfs:label'] = self.name
219228
graph['schema:domainIncludes'] = {'@id': 'ldss:' + self.term_set.iri}
220229
context['schema'] = 'https://schema.org/'
221230
context['rdfs'] = 'http://www.w3.org/2000/01/rdf-schema#'
222231
context['rdf'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
223232
# return the graph and context
224-
return {'@context': context, '@graph': [graph, ]}
233+
return {context_tag: context, graph_tag: [graph, ]}
225234

226235
def path(self):
227236
"""Get the path of the Term"""
@@ -294,7 +303,8 @@ def clean(self):
294303

295304
if self.schema_file:
296305
# scan file for malicious payloads
297-
cd = clamd.ClamdUnixSocket()
306+
cd = clamd.ClamdNetworkSocket(host="clamd.clamav", port=3310,
307+
timeout=10)
298308
json_file = self.schema_file
299309
scan_results = cd.instream(json_file)['stream']
300310
if 'OK' not in scan_results:
@@ -391,13 +401,15 @@ def clean(self):
391401
if self.schema_mapping_file:
392402
json_file = self.schema_mapping_file
393403
# scan file for malicious payloads
394-
cd = clamd.ClamdUnixSocket()
404+
cd = clamd.ClamdNetworkSocket(host="clamd.clamav",
405+
port=3310, timeout=10)
395406
scan_results = cd.instream(json_file)['stream']
396407
if 'OK' not in scan_results:
397408
for issue_type, issue in [scan_results, ]:
398409
logger.error(
399410
'%s %s in transform %s to %s',
400-
issue_type, issue, self.source_schema.iri, self.target_schema.iri # noqa: E501
411+
issue_type, issue, self.source_schema.iri,
412+
self.target_schema.iri # noqa: E501
401413
)
402414
# only load json if no issues found
403415
else:

app/core/tests/test_models_unit.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def test_schema_ledger_virus(self):
7171
patch('core.models.clamd') as clam:
7272
clam.instream.return_value = {'stream': ('BAD', 'EICAR')}
7373
clam.ClamdUnixSocket.return_value = clam
74+
clam.ClamdNetworkSocket.return_value = clam
7475

7576
self.assertEqual(schema.version, '')
7677
self.assertEqual(schema.schema_file.size, len(EICAR))
@@ -112,6 +113,7 @@ def test_schema_ledger_non_json(self):
112113
magic.from_file.return_value = 'text/plain'
113114
clam.instream.return_value = {'stream': ('OK', 'OKAY')}
114115
clam.ClamdUnixSocket.return_value = clam
116+
clam.ClamdNetworkSocket.return_value = clam
115117

116118
self.assertEqual(schema.version, '')
117119
self.assertEqual(schema.schema_file.size, len(file_contents))
@@ -153,6 +155,7 @@ def test_schema_ledger_bleach(self):
153155
magic.from_file.return_value = 'application/json'
154156
clam.instream.return_value = {'stream': ('OK', 'OKAY')}
155157
clam.ClamdUnixSocket.return_value = clam
158+
clam.ClamdNetworkSocket.return_value = clam
156159

157160
self.assertEqual(schema.version, '')
158161
self.assertEqual(schema.schema_file.size, len(file_contents))
@@ -207,6 +210,7 @@ def test_transformation_ledger_virus(self):
207210
patch('core.models.clamd') as clam:
208211
clam.instream.return_value = {'stream': ('BAD', 'EICAR')}
209212
clam.ClamdUnixSocket.return_value = clam
213+
clam.ClamdNetworkSocket.return_value = clam
210214

211215
self.assertEqual(mapping.schema_mapping_file.size, len(EICAR))
212216
mapping.clean()
@@ -242,6 +246,7 @@ def test_transformation_ledger_non_json(self):
242246
magic.from_file.return_value = 'text/plain'
243247
clam.instream.return_value = {'stream': ('OK', 'OKAY')}
244248
clam.ClamdUnixSocket.return_value = clam
249+
clam.ClamdNetworkSocket.return_value = clam
245250

246251
self.assertEqual(mapping.schema_mapping_file.size,
247252
len(file_contents))
@@ -279,6 +284,7 @@ def test_transformation_ledger_bleach(self):
279284
magic.from_file.return_value = 'application/json'
280285
clam.instream.return_value = {'stream': ('OK', 'OKAY')}
281286
clam.ClamdUnixSocket.return_value = clam
287+
clam.ClamdNetworkSocket.return_value = clam
282288

283289
self.assertEqual(mapping.schema_mapping_file.size,
284290
len(file_contents))

app/openlxp_xss_project/settings.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,23 @@
2626
SECRET_KEY = os.environ.get('SECRET_KEY_VAL')
2727

2828
# SECURITY WARNING: don't run with debug turned on in production!
29-
DEBUG = True
29+
DEBUG = False
3030

3131
mimetypes.add_type("text/css", ".css", True)
3232

33-
ALLOWED_HOSTS = ['*']
33+
ALLOWED_HOSTS = os.environ.get('HOSTS').split(';')
34+
35+
# Content Security Policy (CSP)
36+
SELF_VALUE = "'self'" # defining a constant
37+
IMG_DATA_VALUE = "data:"
38+
39+
CSP_DEFAULT_SRC = (SELF_VALUE)
40+
CSP_SCRIPT_SRC = (SELF_VALUE,)
41+
CSP_IMG_SRC = (SELF_VALUE, IMG_DATA_VALUE)
42+
CSP_STYLE_SRC = (SELF_VALUE)
43+
CSP_FRAME_SRC = (SELF_VALUE,)
44+
CSP_FONT_SRC = (SELF_VALUE,)
45+
3446

3547
# Application definition
3648

@@ -49,7 +61,7 @@
4961
'rest_framework.authtoken',
5062
'core.apps.CoreConfig',
5163
'api',
52-
'health_check',
64+
'health_check',
5365
'users',
5466
'social_django',
5567
'openlxp_authentication',
@@ -64,8 +76,19 @@
6476
'django.contrib.messages.middleware.MessageMiddleware',
6577
'django.middleware.clickjacking.XFrameOptionsMiddleware',
6678
'django.contrib.admindocs.middleware.XViewMiddleware',
79+
'csp.middleware.CSPMiddleware',
6780
]
6881

82+
83+
# CORS_ALLOWED_ORIGINS = [os.environ.get('CORS_ALLOWED_ORIGINS')]
84+
# CORS_ALLOW_CREDENTIALS = True
85+
86+
SESSION_COOKIE_SECURE = True
87+
SECURE_BROWSER_XSS_FILTER = True
88+
SECURE_HSTS_SECONDS = 31536000
89+
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
90+
91+
6992
ROOT_URLCONF = 'openlxp_xss_project.urls'
7093

7194
TEMPLATES = [
@@ -188,10 +211,14 @@
188211
],
189212
}
190213

191-
CSRF_COOKIE_DOMAIN = '.deloitteopenlxp.com'
192-
CSRF_TRUSTED_ORIGINS = ['https://dev-ldss.deloitteopenlxp.com', ]
214+
CSRF_COOKIE_HTTPONLY = True
215+
CSRF_COOKIE_SECURE = True
216+
if os.environ.get('CSRF_TRUSTED_ORIGINS'):
217+
CSRF_TRUSTED_ORIGINS = os.environ.get('CSRF_TRUSTED_ORIGINS').split(';')
218+
else:
219+
CSRF_TRUSTED_ORIGINS = ['http://localhost:8010']
193220

194-
SECURE_SSL_REDIRECT = False
221+
# SECURE_SSL_REDIRECT = True
195222

196223
AUTHENTICATION_BACKENDS = (
197224
'django.contrib.auth.backends.ModelBackend',

0 commit comments

Comments
 (0)