Incorrect inverse relationship type in CRE internal-link export

Abierto Apto para principiantes
#1,215 1 comentario 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
78/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python
Área
backend

Línea de trabajo

Empieza con Node_collection.export() en application/database/db.py y compara cómo maneja los enlaces del lado hijo con _assemble_cre_internal_links() y LinkTypes.opposite() en application/defs/cre_defs.py. Actualiza test_export() en application/tests/db_test.py para que espere PartOf y compare los valores anidados de ambos documentos exportados. La tarea estará terminada cuando el enlace del hijo sea PartOf, el del padre siga siendo Contains y la prueba específica de exportación verifique ambos.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Description

In application/database/db.py, Node_collection.export() serializes child-to-parent internal CRE links with the same Contains relationship type as the parent-to-child direction, rather than serializing the inverse relationship type (Is Part Of).

As a result, exported child CRE YAML files claim to "Contain" their parent CRE instead of stating that they are "Part Of" their parent.


Expected Behavior

When an internal parent-child relationship is exported:

  • Parent CRE → Child CRE: Contains (ltype: Contains)
  • Child CRE → Parent CRE: Is Part Of (ltype: Is Part Of, representing LinkTypes.PartOf)

A parent contains a child, and the child is part of the parent.


Actual Behavior

Both exported YAML files are serialized with ltype: Contains. The child document's link back to its parent explicitly states ltype: Contains.


Reproduction Steps

  1. Check out the current upstream commit:

    • Repository: OWASP/OpenCRE
    • Commit: ace5f0bdc4031ed805fd550d13f6a5931a91606d (main)
  2. Run the existing database export unit test:

    python -m unittest application.tests.db_test.TestDB.test_export -v
    
  3. Observe test result:
    The test passes (Ran 1 test in ... OK).

  4. Inspect the generated YAML files written to the temporary directory (111-000.yaml and 111-001.yaml).

  5. Observe the child-side relationship in 111-000.yaml:
    Its link to parent 111-001 (GroupName) is serialized as ltype: Contains.


Evidence

Parent File (111-001.yaml) — Line 6
description: Groupdesc
doctype: CRE
id: 111-001
links:
- document:
    description: CREdesc
    doctype: CRE
    id: 111-000
    name: CREname
  ltype: Contains
name: GroupName
Child File (111-000.yaml) — Line 6 (Defect)
description: CREdesc
doctype: CRE
id: 111-000
links:
- document:
    description: Groupdesc
    doctype: CRE
    id: 111-001
    name: GroupName
  ltype: Contains
name: CREname

Technical Analysis

1. Database Storage vs. Export Logic

In application/database/db.py:

  • Internal CRE relationships are stored in the cre_links table (InternalLinks model), where group stores the parent ID, cre stores the child ID, and type is "Contains".
  • In Node_collection.export() (lines 2210–2236), the internal link loop handles both directions:
    • Parent side (lines 2218–2222):
      grp.add_link(
          cre_defs.Link(
              ltype=cre_defs.LinkTypes.from_str(type), document=CREfromDB(cre)
          )
      )
      
    • Child side (lines 2232–2236):
      c.add_link(
          cre_defs.Link(
              ltype=cre_defs.LinkTypes.from_str(type), document=CREfromDB(group)
          )
      )
      
    Because type is passed directly without inverting, both documents receive Contains.
2. Inconsistency with Hydration Path

In the same file, the read/hydration method _assemble_cre_internal_links() (lines 2162–2172) already implements the correct inverse mapping when reading internal links for a child CRE:

if internal_link.cre == cre.id:
    linked_cre = cres_by_id.get(internal_link.group)
    if not linked_cre:
        continue
    if link_type == cre_defs.LinkTypes.Contains:
        links.append(
            cre_defs.Link(
                ltype=cre_defs.LinkTypes.PartOf,
                document=CREfromDB(linked_cre),
            )
        )

The export path diverges from this existing hydration logic.

3. Enum Mapping Available

application/defs/cre_defs.py defines LinkTypes.opposite() (lines 219–235), which explicitly defines Contains and PartOf as mutual inverses:

if typ == cls.Contains:
    return cls.PartOf
elif typ == cls.PartOf:
    return cls.Contains

However, export() never calls this helper.

4. Why test_export() Misses the Issue

The test test_export() in application/tests/db_test.py passes due to two factors:

  1. Fixture expectation (line 263): expected[1] explicitly specifies ltype=defs.LinkTypes.Contains for the child's link back to the parent, encoding the defect into the expected test data.
  2. Assertion method (line 321): While the parent document is checked using self.assertDictEqual(group, doc) (line 311), the child document is checked using self.assertCountEqual(cre, doc) (line 321). In Python's unittest, calling assertCountEqual on dictionaries compares dictionary keys (['doctype', 'id', 'description', 'name', 'links']) rather than nested values. As long as top-level keys match, the assertion succeeds without validating link types.

Suggested Fix

  1. Update Node_collection.export() to serialize the reverse side of directional internal relationships using the corresponding inverse relationship type. For example, Contains should become PartOf when exporting the child-to-parent link.
  2. Preserve relationship types that are symmetric, according to their defined semantics.
  3. Update test_export() to expect LinkTypes.PartOf for the child-to-parent relationship.
  4. Use deep dictionary comparisons for both exported documents so that nested relationship types are validated.
Lenguaje dominante
Python
Estrellas
210
Forks
167
Merge medio
4 d 12 h
PR fusionados (30 d)
28

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de OWASP/OpenCRE

Todos los issues de OWASP/OpenCRE

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.